我有两个班,一个是"就地操作员"覆盖(比如+=
)和另一个通过@property
公开第一个实例。 (注意:这是大大从我的实际代码简化到最小化再现问题。)
class MyValue(object):
def __init__(self, value):
self.value = value
def __iadd__(self, other):
self.value += other
return self
def __repr__(self):
return str(self.value)
class MyOwner(object):
def __init__(self):
self._what = MyValue(40)
@property
def what(self):
return self._what
现在,当我尝试在公开的属性上使用该运算符时:
>>> owner = MyOwner()
>>> owner.what += 2
AttributeError: can't set attribute
我发现这是预期的,因为它试图在owner
上设置属性。 是否有某种方法可以阻止将属性设置为新对象,同时仍允许我(就地)修改其背后的对象,或者这是只是语言的一个怪癖?
(另请参阅this question,但我尝试采用其他方式,最好是,而不是还原为旧式类,因为最终我希望它能够与Python 3一起使用。)
与此同时,我用一种做同样事情的方法解决了这个问题。
class MyValue(object):
# ...
def add(self, other):
self.value += other
>>> owner = MyOwner()
>>> owner.what.add(2)
>>> print(owner.what)
42
答案 0 :(得分:5)
这是语言的怪癖; object += value
操作转换为:
object = object.__iadd__(value)
这是必要的,因为并非所有对象都是可变的。你的是,并且正确地返回self
,导致上述操作的赋值部分的虚拟无操作。
在您的情况下,相关的object
也是一个属性,因此执行以下操作:
owner.what = owner.what.__iadd__(2)
除了避免在左侧引用object.what
(如tmp = owner.what; tmp += 2
)之外,还有一种方法可以干净利落地处理它。
您可以轻松检测到属性的分配涉及同一个对象并对其进行门控:
class MyOwner(object):
def __init__(self):
self._what = MyValue(40)
@property
def what(self):
return self._what
@what.setter
def what(self, newwhat):
if newwhat is not self._what:
raise AttributeError("can't set attribute")
# ignore the remainder; the object is still the same
# object *anyway*, so no actual assignment is needed
演示:
>>> owner = MyOwner()
>>> owner.what
40
>>> owner.what = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 24, in what
AttributeError: can't set attribute
>>> owner.what += 2
>>> owner.what
42