我看过很多关于这个问题的答案,他们都说不可能在类方法中使用属性,但是下面的代码可以工作:
class A:
@classmethod
@property
def hello(cls):
print(1234)
>>> A.hello
1234
它为什么和如何工作?
在CPython 3.9.1上运行
从Python 3.9开始,类方法触发描述符协议。来自Python文档:
hasattr(obj, '__get__')
的代码路径在Python 3.9中添加,使classmethod()
能够支持链式装饰器。
令人惊讶的是,深入研究这个主题将显示classmethod
触发描述符__get__
,类本身作为实例:
class Descriptor:
def __get__(self, instance, owner):
print(instance, owner)
def __set__(self, value, owner):
print(value, owner)
class A:
regular = Descriptor()
clsmethod = classmethod(Descriptor())
>>> A.regular
None <class '__main__.A'>
>>> A.clsmethod
<class '__main__.A'> None
我猜他们是专门为支持@property
这样的描述符而做的,因为通过类访问它们会返回属性本身:
class B:
@property
def prop(self):
print(self)
>>> B.__dict__["prop"].__get__(None, 1234)
<property object at 0x000001BEEB635630>
>>> B.__dict__["prop"].__get__(1234, None)
1234
如果您希望同时支持classmethod
和普通描述符,这有点不直观,并且会使描述符协议变得笨拙,因为您必须检查owner
是否为None
。
请记住,__set__
是而不是(因为描述符协议在设置类属性时不会调用它),使您无法使用@property.setter
:
>>> A.regular = 1234
>>> A.regular
1234
>>> A.clsmethod = 1234
>>> A.clsmethod
1234