在类方法上使用property()
我有一个类,这个类里面有两个类方法(使用 classmethod()
函数)来获取和设置一个基本上是静态变量的东西。我尝试用 property()
函数来处理这些方法,但结果出现了错误。我在解释器中用以下代码复现了这个错误:
class Foo(object):
_var = 5
@classmethod
def getvar(cls):
return cls._var
@classmethod
def setvar(cls, value):
cls._var = value
var = property(getvar, setvar)
我可以演示这些类方法,但它们作为属性时并不工作:
>>> f = Foo()
>>> f.getvar()
5
>>> f.setvar(4)
>>> f.getvar()
4
>>> f.var
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
>>> f.var=5
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
是否可以将 property()
函数与用 @classmethod
装饰的函数一起使用?
19 个回答
108
我希望这个超级简单的只读 @classproperty
装饰器能帮助到那些在寻找类属性的人。
class classproperty(property):
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class C(object):
@classproperty
def x(cls):
return 1
assert C.x == 1
assert C().x == 1
151
更新: 在 Python 3.13 中,连用 @classmethod
和 @property
的功能被 移除了。
在 Python 3.9 时,你可以把这两个一起用,但正如 @xgt 在 评论 中提到的,这种用法在 Python 3.11 中被 弃用了,所以 现在不再支持(不过可能在一段时间内还能用,或者将来会重新引入)。
你可以查看这里的版本说明:
https://docs.python.org/3.11/library/functions.html#classmethod
不过,之前的用法是这样的:
class G:
@classmethod
@property
def __doc__(cls):
return f'A doc for {cls.__name__!r}'
顺序很重要 - 由于描述符的交互方式,@classmethod
必须放在最上面。
194
3.8 < Python < 3.11
可以同时使用这两个装饰器。具体可以参考 这个回答。
Python 2 和 Python 3(在 3.9-3.10 也适用)
在一个类上创建了一个属性,但这个属性影响的是类的实例。所以如果你想要一个 classmethod
属性,就要在元类上创建这个属性。
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... pass
... @classmethod
... def getvar(cls):
... return cls._var
... @classmethod
... def setvar(cls, value):
... cls._var = value
...
>>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func)
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
不过既然你已经在使用元类了,把类方法直接放到元类里会更好理解。
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
或者,使用 Python 3 的 metaclass=...
语法,并且元类定义在 foo
类体外,元类负责设置 _var
的初始值:
>>> class foo_meta(type):
... def __init__(cls, *args, **kwargs):
... cls._var = 5
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> class foo(metaclass=foo_meta):
... pass
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3