当我在只读属性上使用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通过引用传递对象,那么为什么属性必须是可读的呢?第二,为什么我的虚拟制定者工作呢?
答案 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__
方法已定义并就地修改了列表。