我有一个带有两个类方法的类(使用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装饰函数?
尝试一下,它可以在不更改/添加大量现有代码的情况下完成工作。
>>> class foo(object):
... _var = 5
... def getvar(cls):
... return cls._var
... getvar = classmethod(getvar)
... def setvar(cls, value):
... cls._var = value
... setvar = classmethod(setvar)
... var = property(lambda self: self.getvar(), lambda self, val: self.setvar(val))
...
>>> f = foo()
>>> f.var
5
>>> f.var = 3
>>> f.var
3
属性函数需要两个可调用参数。给它们lambda包装器(它将实例作为第一个参数传递),一切都很好。
没有合理的方法使这个“类属性”系统在Python中工作。
这里有一个不合理的方法。当然,您可以通过增加元类魔法使其更加无缝。
class ClassProperty(object):
def __init__(self, getter, setter):
self.getter = getter
self.setter = setter
def __get__(self, cls, owner):
return getattr(cls, self.getter)()
def __set__(self, cls, value):
getattr(cls, self.setter)(value)
class MetaFoo(type):
var = ClassProperty('getvar', 'setvar')
class Foo(object):
__metaclass__ = MetaFoo
_var = 5
@classmethod
def getvar(cls):
print "Getting var =", cls._var
return cls._var
@classmethod
def setvar(cls, value):
print "Setting var =", value
cls._var = value
x = Foo.var
print "Foo.var = ", x
Foo.var = 42
x = Foo.var
print "Foo.var = ", x
问题的症结在于,属性就是Python所说的“描述符”。没有简单的方法来解释这种元编程是如何工作的,所以我必须指向描述符howto。
只有当您正在实现一个相当高级的框架时,才需要了解这类事情。比如透明对象持久化或RPC系统,或者一种领域特定的语言。
然而,在对之前答案的评论中,你说你
需要修改一个属性,使其能够被类的所有实例看到,并且在调用这些类方法的作用域中没有对类的所有实例的引用。
在我看来,您真正想要的是Observer设计模式。
在读Python 2.2发布说明时,我发现了以下内容。
属性的get方法不会被调用
属性作为类访问
属性(C.x)而不是作为一个
实例属性(C().x)。如果你
想要重写__get__操作
当属性用作类时
属性,可以子类化属性-
它本身就是一种新型的字体
扩展它的__get__方法,或者你可以
从头定义描述符类型
通过创建一个新样式的类
定义__get__, __set__和
__delete__方法。
注意:下面的方法实际上并不适用于setter,只适用于getter。
因此,我认为指定的解决方案是创建一个ClassProperty作为property的子类。
class ClassProperty(property):
def __get__(self, cls, owner):
return self.fget.__get__(None, owner)()
class foo(object):
_var=5
def getvar(cls):
return cls._var
getvar=classmethod(getvar)
def setvar(cls,value):
cls._var=value
setvar=classmethod(setvar)
var=ClassProperty(getvar,setvar)
assert foo.getvar() == 5
foo.setvar(4)
assert foo.getvar() == 4
assert foo.var == 4
foo.var = 3
assert foo.var == 3
然而,setter实际上不起作用:
foo.var = 4
assert foo.var == foo._var # raises AssertionError
foo。_var没有改变,您只是用一个新值重写了属性。
你也可以使用ClassProperty作为装饰器:
class foo(object):
_var = 5
@ClassProperty
@classmethod
def var(cls):
return cls._var
@var.setter
@classmethod
def var(cls, value):
cls._var = value
assert foo.var == 5
一半的解决方案,类上的__set__仍然不起作用。解决方案是一个同时实现属性和静态方法的自定义属性类
class ClassProperty(object):
def __init__(self, fget, fset):
self.fget = fget
self.fset = fset
def __get__(self, instance, owner):
return self.fget()
def __set__(self, instance, value):
self.fset(value)
class Foo(object):
_bar = 1
def get_bar():
print 'getting'
return Foo._bar
def set_bar(value):
print 'setting'
Foo._bar = value
bar = ClassProperty(get_bar, set_bar)
f = Foo()
#__get__ works
f.bar
Foo.bar
f.bar = 2
Foo.bar = 3 #__set__ does not