Python metaclass - make class property accessible via class and class instance

余生颓废 提交于 2021-01-27 06:55:35

问题


Using python 3.7, I have created a class property in a metaclass. I would like to be able to access the property via the class itself or an instantiated object of the class. I can emulate it by creating a class property AND an instance property, but it screws with PyCharm's type hinting. Here's what I consider the ideal set up:

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


class A(metaclass=Meta):
    pass

But unfortunately, here are the results:

>>> 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'

Adding an instance property to A that calls to the class property works at runtime, but PyCharm's type introspection gets confused (it starts treating A.cls_prop as a property instead of a 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'

Is there a better way to do this?


回答1:


I think what you are trying to do is better accomplished using a parent class:

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>

If you also want to be able to access A.cls_prop directly on the class (i.e. without creating an instance), you might want to look at this other question: @staticmethod with @property




回答2:


The problem you are facing is that property, howver a handy thing in Python, is not designed to fullfill all possible use cases.

It is a nice use of the descriptor protocol, and is meant for instance properties, and just remains accessible as itself when called from the class.

Having an anologue to property that would work both from the class and from the instance is just a matter of writting a class with a proper __get__ method - for a fixed string, it could be as simples as:

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

class MyClass:
   cls_prop = MyProperty("foo")

(the big difference to property is returning the value even is "instance" is None)

And, if you want, you can re-implement property 's niceties, such as be able to work as a decorator, and have decorators for the setter and deleter as well.



来源:https://stackoverflow.com/questions/59894757/python-metaclass-make-class-property-accessible-via-class-and-class-instance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!