4

我有两个类,一个具有“就地运算符”覆盖(例如+=),另一个通过@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
4

1 回答 1

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
于 2016-07-22T16:54:19.603 回答