将描述符或属性与类方法一起使用
Using descriptors or properties with classmethod
我看过很多关于这个问题的答案,他们都说不可能将 属性 与类方法一起使用,但以下代码有效:
class A:
@classmethod
@property
def hello(cls):
print(1234)
>>> A.hello
1234
为什么以及如何工作?
运行 在 CPython 3.9.1 上。
从 Python 3.9 开始,class方法触发 descriptor protocol. From the Python docs:
The code path for hasattr(obj, '__get__')
was added in Python 3.9 and makes it possible for classmethod()
to support chained decorators.
令人惊讶的是,深入研究该主题会告诉您 classmethod
触发描述符 __get__
,其中 class 本身作为实例:
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 returns 属性 本身访问它们:
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__
未被调用 (因为描述符协议在设置 class 属性时不会调用它),使您无法使用@property.setter
:
>>> A.regular = 1234
>>> A.regular
1234
>>> A.clsmethod = 1234
>>> A.clsmethod
1234
我看过很多关于这个问题的答案,他们都说不可能将 属性 与类方法一起使用,但以下代码有效:
class A:
@classmethod
@property
def hello(cls):
print(1234)
>>> A.hello
1234
为什么以及如何工作?
运行 在 CPython 3.9.1 上。
从 Python 3.9 开始,class方法触发 descriptor protocol. From the Python docs:
The code path for
hasattr(obj, '__get__')
was added in Python 3.9 and makes it possible forclassmethod()
to support chained decorators.
令人惊讶的是,深入研究该主题会告诉您 classmethod
触发描述符 __get__
,其中 class 本身作为实例:
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 returns 属性 本身访问它们:
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__
未被调用 (因为描述符协议在设置 class 属性时不会调用它),使您无法使用@property.setter
:
>>> A.regular = 1234
>>> A.regular
1234
>>> A.clsmethod = 1234
>>> A.clsmethod
1234