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


当前回答

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

认真对待。

在这种情况下使用类方法的原因是什么?为什么不使用普通类的普通对象呢?


如果你只是想改变值,属性并不是很有用,不是吗?只需要设置属性值就可以了。

只有在需要隐藏某些内容时才应该使用属性——这些内容在未来的实现中可能会改变。

也许你的例子被简化了,你漏掉了一些可怕的计算。但看起来这处房产并没有增加多少价值。

受java影响的“隐私”技术(在Python中,属性名以_开头)并不是很有用。谁的隐私?当您拥有源代码时,private的意义有点模糊(就像在Python中那样)。

受Java影响的ejb风格的getter和setter(通常在Python中作为属性完成)是为了方便Java的基本内省以及通过静态语言编译器的检查。所有这些getter和setter在Python中都没有那么有用。

其他回答

基于https://stackoverflow.com/a/1800999/2290820


class MetaProperty(type):

    def __init__(cls, *args, **kwargs):
        super()

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

    @praparty.setter
    def praparty(cls, val):
        cls._var = val


class A(metaclass=MetaProperty):
    _var = 5


print(A.praparty)
A.praparty = 6
print(A.praparty)

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

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

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

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

如果你想通过一个实例化的对象访问class属性,只在元类上设置它是没有帮助的,在这种情况下,你需要在对象上安装一个普通的属性(它分配给class属性)。我认为以下几点说得更清楚一些:

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

一半的解决方案,类上的__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