0

我已经看到了很多关于这个问题的答案,他们都说不可能将属性与类方法一起使用,但以下代码有效:

class A:
    @classmethod
    @property
    def hello(cls):
        print(1234)

>>> A.hello
1234

为什么以及如何工作?

在 CPython 3.9.1 上运行。

4

1 回答 1

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,因为您必须检查 is 是否ownerNone.

但是请记住, __set__被调用(因为描述符协议在设置类属性时不会调用它),使您无法使用@property.setter

>>> A.regular = 1234
>>> A.regular
1234
>>> A.clsmethod = 1234
>>> A.clsmethod
1234
于 2021-02-08T21:21:20.113 回答