在classmethods使用属性()(Using property() on classmetho

2019-06-18 12:06发布

我有两个类方法(使用类方法()函数)用于获取和设置什么本质上是一个静态变量的类。 我试图用属性()函数与这些,但它导致错误。 我能够重现错误与解释如下:

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

是否有可能使用与类方法装饰功能属性()函数?

Answer 1:

阅读的Python 2.2版本的笔记,我发现下面。

[属性的] get方法将不被该属性时作为一类属性(CX),而不是作为一个实例属性访问(C()。x)的调用。 如果你想为一个类属性时来覆盖性能__get__操作,你也可以继承财产 - 这是一种新的风格类型本身 - 延长其__get__方法,或者你可以通过创建一个新定义一个从无到有的描述符类型式的类,定义__get__,__set__和__delete__方法。

注:以下方法实际上并不用于制定者,只有干将工作。

因此,笔者认为在规定的解决方案是创建一个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

然而,制定者不实际工作:

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


Answer 2:

一个属性在类创建的,但影响的实例。 所以,如果你想有一个类方法属性,创建于元类的属性。

>>> 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

但是,由于您使用的是元类,无论如何,它会读取更好,如果你只是移动classmethods在那里。

>>> 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


Answer 3:

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


Answer 4:

是否有可能使用与类方法装饰功能属性()函数?

没有。

然而,一个类方法是简单地从该类的实例可访问的类绑定方法(部分功能)。

由于实例类的功能,你可以从实例派生类,可以可以得到任何想要的行为,你可能会从一个类属性与希望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
<class '__main__.FooClassProperty'>

和类

>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>

可以访问类属性。



Answer 5:

Python 3中!

老问题,很多的意见,迫切需要一个真Python 3的方式。

幸运的是,它很容易与metaclass kwarg:

class FooProperties(type):

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

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

然后, >>> Foo.var

'FOO!'



Answer 6:

有没有合理的办法让这个“类属性”系统在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系统,或者说是一种特定于域的语言。

然而,到以前的答案评论,你说你

需要修改的属性,在由一个类的所有实例可见,并从这些类的方法被称为没有在类的所有实例的引用范围的这样一种方式。

在我看来,你真正想要的是一个观察者设计模式。



Answer 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


Answer 8:

半个解决方案,__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


Answer 9:

因为我需要修改的属性,在由一个类的所有实例可见,并从这些类的方法被称为没有在类的所有实例的引用范围的这样一种方式。

你有没有到类的至少一个实例访问? 我能想到的办法做到这一点,那么:

class MyClass (object):
    __var = None

    def _set_var (self, value):
        type (self).__var = value

    def _get_var (self):
        return self.__var

    var = property (_get_var, _set_var)

a = MyClass ()
b = MyClass ()
a.var = "foo"
print b.var


Answer 10:

试试这个,它获取而无需更改/增添了不少的现有代码所做的工作。

>>> 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

property功能需要两个callable参数。 给他们的λ包装(它传递的实例作为第一个参数)和一切都很好。



Answer 11:

下面是其应通过其采用了元类的实例通过类和访问都访问工作的解决方案。

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

这也适用于在元类中定义的制定者。



Answer 12:

寻找不同的地方后,我发现定义classproperty与Python 2和3个有效的方法。

from future.utils import with_metaclass

class BuilderMetaClass(type):
    @property
    def load_namespaces(self):
        return (self.__sourcepath__)

class BuilderMixin(with_metaclass(BuilderMetaClass, object)):
    __sourcepath__ = 'sp'        

print(BuilderMixin.load_namespaces)

希望这可以帮助别人:)



Answer 13:

这里是我的建议。 不要使用类的方法。

认真。

什么是使用在这种情况下,类方法的原因吗? 为什么不能有一个普通类的普通对象?


如果你只是想改变数值,属性是不是真的非常有帮助的是什么呢? 刚刚成立的属性值,并用它做。

一些可能在未来实现改变 - 如果有什么隐瞒,才应使用的属性。

也许你的例子方式剥离下来,并有你留下了一些地狱般的计算。 但它看起来并不像财产增加了显著的价值。

Java的影响的“隐私”技术(在Python,属性与_开头的名称)是不是真的很有帮助。 私人从谁? 私人的问题是有点含糊不清的,当你有源(如你在Python做的。)

Java的影响的EJB风格的getter和setter(常做的是在Python特性)是为了促进Java的基本内省以及通过与静态语言的编译器鼓起。 所有这些getter和setter不是用Python一样有用。



文章来源: Using property() on classmethods
标签: python oop