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


当前回答

我希望这个极其简单的只读@classproperty装饰器能够帮助人们查找类属性。

class classproperty(property):
    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

其他回答

Python 3 !

参见@Amit Portnoy的回答,在python >= 3.9中有一个更清晰的方法


老问题,很多观点,迫切需要一个唯一正确的Python 3方式。

幸运的是,使用元类kwarg很简单:

class FooProperties(type):
    
    @property
    def var(cls):
        return cls._var

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

然后>>> foo。var

“喷火!”

因为我需要修改一个属性,以一种可以被类的所有实例看到的方式,并且在调用这些类方法的作用域中没有对类的所有实例的引用。

您是否至少可以访问该类的一个实例?我可以想到一个办法:

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

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

我希望这个极其简单的只读@classproperty装饰器能够帮助人们查找类属性。

class classproperty(property):
    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

尝试一下,它可以在不更改/添加大量现有代码的情况下完成工作。

>>> 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包装器(它将实例作为第一个参数传递),一切都很好。