如何使只读属性可变?

cri*_*ica 4 python properties new-style-class python-2.7 augmented-assignment

我有两个类,一个是"就地操作符"覆盖(比如说+=),另一个是通过a公开第一个实例@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
Run Code Online (Sandbox Code Playgroud)

现在,当我尝试在公开的属性上使用该运算符时:

>>> owner = MyOwner()
>>> owner.what += 2
AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)

从我发现这是预期的,因为它试图设置属性owner. 有没有办法防止属性设置为新对象,同时仍然允许我(就地)修改它后面的对象,或者这只是语言的怪癖?

(另请参阅此问题,但我试图采用其他方式,最好不要回到旧式类,因为最终我希望它能与Python 3一起使用.)


与此同时,我用一种方法做了同样的事情.

class MyValue(object):
    # ... 

    def add(self, other):
        self.value += other

>>> owner = MyOwner()
>>> owner.what.add(2)
>>> print(owner.what)
42
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 5

这是语言的怪癖; 该object += value操作转换为:

object = object.__iadd__(value)
Run Code Online (Sandbox Code Playgroud)

这是必要的,因为并非所有对象都是可变的.你的是,正确返回self导致上述操作的赋值部分的虚拟无操作.

在您的情况下,object问题也是一个属性,因此执行以下操作:

owner.what = owner.what.__iadd__(2)
Run Code Online (Sandbox Code Playgroud)

除了避免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
Run Code Online (Sandbox Code Playgroud)

演示:

>>> 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
Run Code Online (Sandbox Code Playgroud)