Python метакласс - сделать свойство класса доступным через класс и экземпляр класса - PullRequest
4 голосов
/ 24 января 2020

Используя python 3.7, я создал свойство класса в метаклассе. Я хотел бы иметь возможность получить доступ к свойству через сам класс или экземпляр объекта класса. Я могу эмулировать его, создав свойство класса И свойство экземпляра, но оно порождает подсказку типа PyCharm. Вот что я считаю идеальной настройкой:

class Meta(type):
    @property
    def cls_prop(cls) -> str:
        return 'foo'


class A(metaclass=Meta):
    pass

Но, к сожалению, вот результаты:

>>> A.cls_prop
'foo'
>>> a = A()
>>> a.cls_prop
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'cls_prop'

Добавление свойства экземпляра в A, которое вызывает свойство класса, работает во время выполнения, но самоанализ типа PyCharm запутывается (он начинает трактовать A.cls_prop как property вместо str):

class A(metaclass=Meta):
  @property
  def cls_prop(self) -> str:
      return self.__class__.cls_prop

>>> A.cls_prop
'foo'
>>> a = A()
>>> a.cls_prop
'foo'

Есть ли лучший способ сделать это?

Ответы [ 2 ]

1 голос
/ 24 января 2020

Я думаю, что то, что вы пытаетесь сделать, лучше сделать с помощью родительского класса:

class Parent:
    @property
    def cls_prop(self):
        return 'foo'

class A(Parent):
    pass

>>> a = A()
>>> a.cls_prop
'foo'
>>>> A.cls_prop
<property object at 0x7f1afcb190e8>

Если вы также хотите иметь возможность доступа к A.cls_prop непосредственно в классе (т.е. без создания экземпляра) ), вы можете посмотреть на этот вопрос: @ staticmethod с @ property

0 голосов
/ 24 января 2020

Проблема, с которой вы сталкиваетесь, заключается в том, что property, как бы удобная вещь в Python, не была разработана для полного выполнения всех возможных вариантов использования.

Это хорошее использование протокола дескриптора, и предназначен для свойств экземпляра и просто остается доступным сам по себе при вызове из класса.

Наличие анолога к property, который будет работать как из класса, так и из экземпляра, - это просто вопрос написания класса при правильном методе __get__ - для фиксированной строки это может быть так просто:

class MyProperty:
    def __init__(self, value):
        self.value = value
    def __get__(self, instance, owner):
         return self.value 

class MyClass:
   cls_prop = MyProperty("foo")

(большая разница в свойстве - возвращать значение, даже если "instance" - None) *

И, если хотите, вы можете заново реализовать тонкости свойств, например, иметь возможность работать в качестве декоратора, а также иметь декораторы для setter и deleter.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...