如何在没有setter的属性上使用+ =运算符?

时间:2016-11-03 19:34:41

标签: python python-3.x

当我在只读属性上使用Can't set attribute运算符时,我收到+=错误,该属性属于我已定义的类型__iadd__()方法。

我的代码的简化(但可运行)版本:

class Emitter(list):
    def __iadd__(self, other):
        self.append( other )
        return self

class Widget:
    def __init__(self):
        self._on_mouseenter = Emitter()

    @property
    def on_mouseenter(self): return self._on_mouseenter


my_widget = Widget()
my_widget.on_mouseenter += lambda source: print("on_mouseenter!")

最后一行产生错误。如果我将以下行添加到Widget的定义中,它就会消失:

@on_mouseenter.setter
def on_mouseenter(self, value): pass

(在https://repl.it/EONf/0处运行)

这个行为在两个帐户上看起来很奇怪。首先,我认为Python通过引用传递对象,那么为什么属性必须是可读的呢?第二,为什么我的虚拟制定者工作呢?

2 个答案:

答案 0 :(得分:3)

__iadd__返回一个替换对象以反弹到变量。这当然需要一个二传手。

在这种情况下它可以正常工作,因为你忽略了这个集合,但仍然保留原来的对象,你已经就地改变了。

这种行为是必需的,因为某些对象是不可变的,但是就地添加仍然适用于它们。

i += 5获取i绑定的数字,将其加5,并将i重新绑定到新结果编号。也就是说,它完全等同于i = i + 5,其中包含一个赋值。

答案 1 :(得分:3)

这是由Python augmented assignment operators的工作方式引起的。在调用适当的special method之后,他们将返回值分配给运算符左侧的对象。

  

如果x是a的实例   使用__iadd__()方法的类,x += y相当于x = x.__iadd__(y)。否则,x.__add__(y)y.__radd__(x)是   与x + y的评估一样考虑。

因此

my_widget.on_mouseenter += lambda source: print("on_mouseenter!")

相当于

my_widget.on_mouseenter = my_widget.on_mouseenter.__iadd__(lambda source: print("on_mouseenter!"))

你需要一个setter来执行赋值。但是,它不必做任何事情,因为__iadd__方法已定义并就地修改了列表。