Python中的__iadd__,只读属性

时间:2018-06-01 12:44:28

标签: python

This question处理有关Python读写属性的__iadd__。但是,我很难找到只读属性的解决方案。

在我的MWE中,我们有一个只读属性Beta.value,返回一个Alpha实例。我想我应该可以在__iadd__上使用Beta.value,因为返回的值已就地变异,并且对Beta本身没有任何更改,很多喜欢" beta.value.content +="前面的一行。但是,以下代码与AttributeError: can't set attribute崩溃。

是否可以在只读属性上使用__iadd__

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

3 个答案:

答案 0 :(得分:3)

可以通过为只接受原始对象的属性添加特殊的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

答案 1 :(得分:0)

人们经常忘记使用就地操作员的事情是他们的使用总是涉及一项任务。您可以在Alpha.content(或任何intstr中)直观地看到这一点:整数是不可变的,但操作有效。忘记Alphalist之类的步骤要容易得多,其中就地操作符只返回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.__iadd__不可避免地会返回一个新的引用。

答案 2 :(得分:0)

运算符 += 处理整个分配,整个行。因此存在歧义:您的对象是调用 __iadd__ 还是 setter 属性?看看这个:

这不起作用:

beta.value += 2  # calls setter property (which does not exist)

但这有效:

value = beta.value  # calls getter property
value += 2  # calls __iadd__ (upon the returned object from the getter)

注意:这让我很沮丧,但它必须是这样;否则,我们如何知道调用了哪个方法以及代码的行为?

玩得开心:)