当前位置:  开发笔记 > 编程语言 > 正文

在classmethods上使用property()

如何解决《在classmethods上使用property()》经验,为你挑选了7个好方法。

我有一个类有两个类方法(使用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修饰函数一起使用?



1> A. Coady..:

在类上创建属性但影响实例.因此,如果您需要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


@Josay:首先需要定义元类,然后使用新的`class Foo(metaclass = ...)`语法定义类.

2> Jason R. Coo..:

阅读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的setter部分实际上如所描述的那样工作:虽然示例的断言全部通过,但最后foo._var == 4(不是3,如暗示的那样).设置属性clobbers属性本身.当在[python-dev](http://mail.python.org/pipermail/python-ideas/2011-January/008950.html)上讨论类属性时[有人指出,虽然getter是微不足道的,但是setter很难(不可能?)没有元类](http://mail.python.org/pipermail/python-ideas/2011-January/008959.html)
@Gabriel完全正确.我无法相信没有人指出这两年.

3> Denis Ryzhko..:

我希望这个简单的只读@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


这适用于子类吗?(子类可以覆盖classproperties吗?)
这不是只读的.``Cx = 10; 断言Cx == 10``

4> Aaron Hall..:
是否可以将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

有权访问类属性.



5> OJFord..:
Python 3!

老问题,很多观点,非常需要一个真正的Python 3方式.

幸运的是,对于metaclasskwarg 来说很容易:

class FooProperties(type):

    @property
    def var(cls):
        return cls._var

class Foo(object, metaclass=FooProperties):
    _var = 'FOO!'

然后, >>> Foo.var

'FOO!'



6> ddaa..:

没有合理的方法可以使这个"类属性"系统在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设计模式.



7> 小智..:

如果要通过实例化对象访问类属性,则仅在元类上进行设置无效,在这种情况下,您还需要在对象上安装普通属性(调度到类属性).我认为以下内容更为明确:

#!/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

推荐阅读
oDavid_仔o_880
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有