Python:当访问对象的特定属性时,如何做额外的工作?
假设我有一个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
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
方法raiseAttributeError
,因为通常情况下,您希望自动递增的魔法变量是只读的,但我决定显示更复杂的版本,只是为了显示您必须处理的所有问题。)如果您确实希望类变量具有与@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
方法raiseAttributeError
,因为通常情况下,您希望自动递增的魔法变量是只读的,但我决定显示更复杂的版本,只是为了显示您必须处理的所有问题。)这与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在问题示例中用作实例属性。很可能它不是一个类属性,这是一个非常复杂的属性