我有一个带有两个类方法的类(使用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
是否可以使用属性()函数与@classmethod装饰函数?
这是我的建议。不要使用类方法。
认真对待。
在这种情况下使用类方法的原因是什么?为什么不使用普通类的普通对象呢?
如果你只是想改变值,属性并不是很有用,不是吗?只需要设置属性值就可以了。
只有在需要隐藏某些内容时才应该使用属性——这些内容在未来的实现中可能会改变。
也许你的例子被简化了,你漏掉了一些可怕的计算。但看起来这处房产并没有增加多少价值。
受java影响的“隐私”技术(在Python中,属性名以_开头)并不是很有用。谁的隐私?当您拥有源代码时,private的意义有点模糊(就像在Python中那样)。
受Java影响的ejb风格的getter和setter(通常在Python中作为属性完成)是为了方便Java的基本内省以及通过静态语言编译器的检查。所有这些getter和setter在Python中都没有那么有用。
3.8 < Python < 3.11
可以同时使用这两种装饰器。请看这个答案。
Python < 3.9
属性在类上创建,但会影响实例。所以如果你想要一个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的元类=…语法,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
这里有一个解决方案,它应该既适用于通过类访问,也适用于通过使用元类的实例访问。
In [1]: class ClassPropertyMeta(type):
...: @property
...: def prop(cls):
...: return cls._prop
...: def __new__(cls, name, parents, dct):
...: # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
...: dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
...: dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
...: return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
...:
In [2]: class ClassProperty(object):
...: __metaclass__ = ClassPropertyMeta
...: _prop = 42
...: def __getattr__(self, attr):
...: raise Exception('Never gets called')
...:
In [3]: ClassProperty.prop
Out[3]: 42
In [4]: ClassProperty.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-e2e8b423818a> in <module>()
----> 1 ClassProperty.prop = 1
AttributeError: can't set attribute
In [5]: cp = ClassProperty()
In [6]: cp.prop
Out[6]: 42
In [7]: cp.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-e8284a3ee950> in <module>()
----> 1 cp.prop = 1
<ipython-input-1-16b7c320d521> in <lambda>(cls, attr, val)
6 # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
7 dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
----> 8 dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
9 return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
AttributeError: can't set attribute
这也适用于元类中定义的setter。