__Python中具有只读属性的iadd

__Python中具有只读属性的iadd,python,Python,处理Python读写属性上的\uuu iadd\uu。但是,我正在努力找到只读属性的解决方案 在my MWE中,我们有一个只读属性Beta.value,返回一个Alpha实例。我想我应该能够在Beta.value上使用\uuuu iadd\uuuu,因为返回的值在适当的位置发生了变异,并且对Beta本身没有任何更改,就像它前面的“Beta.value.content+=”行一样。但是,以下代码与属性错误一起崩溃:无法设置属性 是否可以在只读属性上使用\uuuu iadd\uuuu class A

处理Python读写属性上的
\uuu iadd\uu
。但是,我正在努力找到只读属性的解决方案

在my MWE中,我们有一个只读属性
Beta.value
,返回一个
Alpha
实例。我想我应该能够在
Beta.value
上使用
\uuuu iadd\uuuu
,因为返回的值在适当的位置发生了变异,并且对
Beta
本身没有任何更改,就像它前面的“
Beta.value.content+=
”行一样。但是,以下代码与
属性错误一起崩溃:无法设置属性

是否可以在只读属性上使用
\uuuu iadd\uuuu

class Alpha:
    def __init__( self, content : int ) -> None:
        self.content : int = content


    def __iadd__( self, other : int ) -> "Alpha":
        self.content += other
        return self


class Beta:
    def __init__( self ):
        self.__value: Alpha = Alpha(1)


    @property
    def value( self ) -> Alpha:
        return self.__value


beta = Beta()
beta.value.content += 2
beta.value += 2

人们在使用就地运算符时经常忘记的一点是,它们的使用总是涉及到赋值。你可以在
Alpha.content
(或者任何
int
或者
str
中直观地看到这一点):整数是不可变的,但是操作是有效的。对于像
Alpha
list
这样的东西,很容易忘记这一步,其中就地操作符只返回
self
。请记住,运算符可以返回任何内容,并且结果必须绑定到原始名称。这里发生的事情基本上是这样的:

x = beta.value
x = operator.iadd(x, 2)      # Totally fine
beta.value = x # You can  imagine how this would be a problem...
这样做的一个直接后果是,尽管出现了错误,您仍将看到
beta.value
中的更改

欢迎您通过首先分配给临时变量来绕过重新分配,即显式运行上面显示的前两行。请记住,虽然在您的情况下,
Alpha
是可变的,并且确实会在适当的位置修改自身,但这不是一般情况下的要求:

x = beta.value
x += 2
按预期工作。但是,

x = beta.value.content
x += 2

不会,因为
int.\uuuu iadd\uuuu
不可避免地返回一个新引用。

可以通过为只接受原始对象的属性添加特殊的setter来欺骗它

等级
Beta
将变成:

class Beta:
    def __init__( self ):
        self.__value: Alpha = Alpha(1)

    def _get_val( self ) -> Alpha:
        return self.__value
    def _set_val( self, val: Alpha):
        if not (val is self.__value):            # only accept the existing object
            raise AttributeError("can't set attribute")
    value = property(_get_val, _set_val)
有了这个技巧,您可以成功地使用:

>>> beta = Beta()
>>> beta.value.content
1
>>> beta.value = Alpha(2)               # property IS read only
Traceback (most recent call last):
  File "<pyshell#86>", line 1, in <module>
    beta.value = Alpha(2)
  File "<pyshell#78>", line 9, in _set_val
    raise AttributeError("can't set attribute")
AttributeError: can't set attribute
>>> beta.value.content                  # and was not changed by an assignment attempt
1
>>> beta.value += 2                     # but accepts augmented assignment
>>> beta.value.content
3
beta=beta() >>>beta.value.content 1. >>>beta.value=Alpha(2)#属性为只读 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 β值=α(2) 文件“”,第9行,输入值 引发AttributeError(“无法设置属性”) AttributeError:无法设置属性 >>>beta.value.content#并且未被分配尝试更改 1. >>>beta.value+=2#但接受增广赋值 >>>beta.value.content 3.
Related:甚至比这更糟糕:
beta.value
会引发错误(这是预期的),但会增加
beta.value.content
@Aran Fey很好地提醒了我们为什么
x+=1
适用于不可变类型。