如何使只读 属性 可变?

How can I make a read-only property mutable?

我有两个 类,一个带有 "in-place operator" 覆盖(例如 +=),另一个通过 @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

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

object = object.__iadd__(value)

这是必要的,因为并非所有对象都是可变的。你的是,并且正确 returns 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