我有一个类有两个类方法(使用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 "", line 1, in ? TypeError: 'classmethod' object is not callable >>> f.var=5 Traceback (most recent call last): File " ", line 1, in ? TypeError: 'classmethod' object is not callable
是否可以将property()函数与classmethod修饰函数一起使用?
在类上创建属性但影响实例.因此,如果您需要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
阅读Python 2.2发行说明,我发现以下内容.
当属性作为类属性(Cx)而不是实例属性(C().x)访问时,将不会调用[属性]的get方法.如果要在用作类属性时覆盖属性的__get__操作,则可以子类化属性 - 它本身是新样式类型 - 以扩展其__get__方法,或者您可以通过创建新的方法从头开始定义描述符类型-style类定义__get __,__ set__和__delete__方法.
注意:以下方法实际上不适用于setter,只适用于getter.
因此,我认为规定的解决方案是创建一个ClassProperty作为属性的子类.
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
我希望这个简单的只读@classproperty
装饰器可以帮助某人寻找classproperties.
class classproperty(object): def __init__(self, fget): self.fget = fget 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
是否可以将property()函数与classmethod修饰函数一起使用?
没有.
但是,类方法只是可从该类的实例访问的类的绑定方法(部分函数).
由于实例是类的函数,您可以从实例派生类,因此您可以从类属性中获取您可能需要的任何所需行为property
:
class Example(object): _class_property = None @property def class_property(self): return self._class_property @class_property.setter def class_property(self, value): type(self)._class_property = value @class_property.deleter def class_property(self): del type(self)._class_property
此代码可用于测试 - 它应该通过而不会引发任何错误:
ex1 = Example() ex2 = Example() ex1.class_property = None ex2.class_property = 'Example' assert ex1.class_property is ex2.class_property del ex2.class_property assert not hasattr(ex1, 'class_property')
请注意,我们根本不需要元类 - 并且您无法通过其类的实例直接访问元类.
@classproperty
装饰实际上你可以classproperty
通过子类化在几行代码中创建一个装饰器property
(它在C中实现,但你可以在这里看到等效的Python ):
class classproperty(property): def __get__(self, obj, objtype=None): return super(classproperty, self).__get__(objtype) def __set__(self, obj, value): super(classproperty, self).__set__(type(obj), value) def __delete__(self, obj): super(classproperty, self).__delete__(type(obj))
然后将装饰器视为与属性结合的类方法:
class Foo(object): _bar = 5 @classproperty def bar(cls): """this is the bar attribute - each subclass of Foo gets its own. Lookups should follow the method resolution order. """ return cls._bar @bar.setter def bar(cls, value): cls._bar = value @bar.deleter def bar(cls): del cls._bar
这段代码应该没有错误:
def main(): f = Foo() print(f.bar) f.bar = 4 print(f.bar) del f.bar try: f.bar except AttributeError: pass else: raise RuntimeError('f.bar must have worked - inconceivable!') help(f) # includes the Foo.bar help. f.bar = 5 class Bar(Foo): "a subclass of Foo, nothing more" help(Bar) # includes the Foo.bar help! b = Bar() b.bar = 'baz' print(b.bar) # prints baz del b.bar print(b.bar) # prints 5 - looked up from Foo! if __name__ == '__main__': main()
但我不确定这会是多么明智.一篇旧的邮件列表文章表明它应该不起作用.
上面的缺点是无法从类中访问"类属性",因为它只会覆盖类中的数据描述符__dict__
.
但是,我们可以使用元类中定义的属性覆盖它__dict__
.例如:
class MetaWithFooClassProperty(type): @property def foo(cls): """The foo property is a function of the class - in this case, the trivial case of the identity function. """ return cls
然后,元类的类实例可以具有使用前面部分中已经演示的原理访问类的属性的属性:
class FooClassProperty(metaclass=MetaWithFooClassProperty): @property def foo(self): """access the class's property""" return type(self).foo
现在我们看到了两个实例
>>> FooClassProperty().foo
和班级
>>> FooClassProperty.foo
有权访问类属性.
老问题,很多观点,非常需要一个真正的Python 3方式.
幸运的是,对于metaclass
kwarg 来说很容易:
class FooProperties(type): @property def var(cls): return cls._var class Foo(object, metaclass=FooProperties): _var = 'FOO!'
然后, >>> Foo.var
'FOO!'
没有合理的方法可以使这个"类属性"系统在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所谓的"描述符".没有简单易懂的方法来解释这种元编程是如何工作的,所以我必须指出描述符如何.
如果要实现相当高级的框架,您只需要了解这类事情.像透明对象持久性或RPC系统,或一种特定于域的语言.
但是,在对先前答案的评论中,您说是您
需要修改一个属性,该属性以一种类的所有实例都可以看到,并且在调用这些类方法的范围内不会引用该类的所有实例.
在我看来,你真正想要的是一个Observer设计模式.
如果要通过实例化对象访问类属性,则仅在元类上进行设置无效,在这种情况下,您还需要在对象上安装普通属性(调度到类属性).我认为以下内容更为明确:
#!/usr/bin/python class classproperty(property): def __get__(self, obj, type_): return self.fget.__get__(None, type_)() def __set__(self, obj, value): cls = type(obj) return self.fset.__get__(None, cls)(value) class A (object): _foo = 1 @classproperty @classmethod def foo(cls): return cls._foo @foo.setter @classmethod def foo(cls, value): cls.foo = value a = A() print a.foo b = A() print b.foo b.foo = 5 print a.foo A.foo = 10 print b.foo print A.foo