1

假设我在 Python 中有一个类:

class Foo(object):
    a = 1
    b = 2

当我访问“a”而不是“b”时,我想做一些额外的事情。因此,例如,让我们假设我想做的额外事情是增加属性的值:

> f = Foo()
> f.a # Should output 2
> f.a # Should output 3
> f.a # Should output 4
> f.b # Should output 2, since I want the extra behavior just on 'a'

感觉好像有办法通过 __getattr__ 或 __getattribute__,但我想不通。

额外的东西可以是任何东西,不一定与属性相关(如 print 'Hello world')。

谢谢。

4

2 回答 2

8

您正在寻找的是一个属性,它可以很好地用作装饰器:

class Foo(object):
    _a = 2

    @property
    def a(self):
        Foo._a += 1
        return Foo._a - 1

    b = 2

每当您尝试访问时都会调用该函数foo_instance.a,并将返回的值用作属性的值。您也可以定义一个setter,在设置属性时使用新值调用它。

这是假设您想要仅从实例访问的类属性的奇怪设置。(_a这里b属于该类-也就是说,所有实例仅共享一个变量-如您的问题所示)。但是,属性始终是实例拥有的。最可能的情况是您实际上想要:

class Foo(object):
    def __init__(self):
        self._a = 2
        self.b = 2

    @property
    def a(self):
        self._a += 1
        return self._a - 1

它们是实例属性。

于 2012-11-13T20:47:51.503 回答
3

如果你真的想要@property类变量的等价物,你必须自己构建描述符

您几乎肯定不想这样做——请参阅 Lattyware 的回答,了解如何制作普通的实例变量,并将其中一个变成@property.

但你可以这样做:

class IncrementOnGetDescriptor(object):
    def __init__(self, initval=None):
        self.val = initval
    def __get__(self, obj, objtype):
        self.val += 1
        return self.val - 1
    def __set__(self, obj, val):
        self.val = val

class Foo(object):
    a = IncrementOnGetDescriptor(2)
    b = 2

现在你可以测试它了:

>>> f = Foo()
>>> f.a
2
>>> Foo.a
3
>>>> f.a
4

把它变成@classproperty装饰器留给读者练习。

PS, this still isn't exactly like a normal class variable. Setting Foo.a = 10 will replace your magic auto-incrementing value with a normal 10, while setting foo.a = 10 will update the class with an auto-incrementing 10 instead of storing an instance variable in f. (I originally had the __set__ method raise AttributeError, because normally you'd want an auto-incrementing magic variable be read-only, but I decided to show the more complex version just to show all the issues you have to deal with.)

于 2012-11-13T21:08:47.103 回答