Python:当访问对象的特定属性时,如何做额外的工作?

Python:当访问对象的特定属性时,如何做额外的工作?,python,Python,假设我有一个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 ext

假设我有一个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'
感觉好像有办法通过uuu getattr uuuuuuuuuuuuu或uuuuuuu getattribute uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

额外的东西可以是任何东西,不一定与属性相关(比如打印“Hello world”)


谢谢。

您要找的是一个可以很好地用作装饰的:

class Foo(object):
    _a = 2

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

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

这是假定您需要仅从实例访问的类属性的奇怪设置。(
\u这里的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

它们是实例属性。

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

class Foo(object):
    _a = 2

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

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

这是假定您需要仅从实例访问的类属性的奇怪设置。(
\u这里的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

它们是实例属性。

如果您确实希望类变量具有与
@property
等价的属性,则必须自己构建

您几乎肯定不想这样做,请参阅Lattyware关于如何生成普通实例变量并将其中一个变量转换为
@属性的答案

但你可以这样做:

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,这仍然不完全像一个普通的类变量。设置
Foo.a=10
将用正常的
10
替换神奇的自动递增值,而设置
Foo.a=10
将用自动递增的
10
更新类,而不是在
f
中存储实例变量。(我最初使用了
\uuu set\uu
方法raise
AttributeError
,因为通常情况下,您希望自动递增的魔法变量是只读的,但我决定显示更复杂的版本,只是为了显示您必须处理的所有问题。)

如果您确实希望类变量具有与
@property
等价的属性,则必须自己构建

您几乎肯定不想这样做,请参阅Lattyware关于如何生成普通实例变量并将其中一个变量转换为
@属性的答案

但你可以这样做:

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,这仍然不完全像一个普通的类变量。设置
Foo.a=10
将用正常的
10
替换神奇的自动递增值,而设置
Foo.a=10
将用自动递增的
10
更新类,而不是在
f
中存储实例变量。(我最初使用
\uuu set\uu
方法raise
AttributeError
,因为通常情况下,您希望自动递增的魔法变量是只读的,但我决定显示更复杂的版本,只是为了显示您必须处理的所有问题。)

这与Django有什么关系?@abarnert它没有,我删除了标记。您创建了
a
b
Foo
的成员,而不是
Foo
的每个实例的成员。这意味着所有实例共享一个副本。这是故意的吗?如果是这样的话,这会使问题变得更加困难…实际上并没有使问题变得更困难-您只需从属性中访问并更改类变量。唯一的问题是,如果您希望该属性可以作为
Foo.a
@abarnert No访问,这不是它的意思。实例上的属性赋值创建/更改实例属性,而不考虑同名的类属性。试试你的例子
f2.b
将是2。这与Django有什么关系?@abarnert没有,我删除了标记。您创建了
a
b
Foo
的成员,而不是
Foo
的每个实例。这意味着所有实例共享一个副本。这是故意的吗?如果是这样的话,这会使问题变得更加困难…实际上并没有使问题变得更困难-您只需从属性中访问并更改类变量。唯一的问题是,如果您希望该属性可以作为
Foo.a
@abarnert No访问,这不是它的意思。实例上的属性赋值创建/更改实例属性,而不考虑同名的类属性。试试你的例子<代码>f2.b
将为2。更改后的属性体是否缺少两个
self的实例。\u
?您已将其作为实例属性,而不是类属性。(例如,当你做
Foo.a
时会发生什么?)@delnan:实际上,
self
不起作用,它必须是
Foo
。另外,
\u a
拼写错误为
a
..@abarnert在问题示例中用作实例属性。很可能它不是一个类属性,这是一个非常复杂的属性