简体   繁体   English

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

[英]How to use the += operator on a property that doesn't have a setter?

I'm getting a Can't set attribute error when I'm using the += operator on a read-only property that is of a type for which I've defined a __iadd__() method. 在只读属性上使用+=运算符时,出现Can't set attribute错误,该属性的类型已定义__iadd__()方法。

Simplified (but runnable) version of my code: 我的代码的简化(但可运行)版本:

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!")

The last line produces the error. 最后一行产生错误。 It goes away if I add the following line to the definition of Widget : 如果Widget下行添加到Widget的定义中,它将消失:

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

(Runnable at https://repl.it/EONf/0 ) (可在https://repl.it/EONf/0运行

This behaviour seems strange on two accounts. 在两个帐户上,此行为似乎很奇怪。 First, I thought that Python passes objects by reference, so why should the property have to be readable? 首先,我认为Python通过引用传递对象,那么为什么该属性必须是可读的? And second, how come that my dummy setter even works? 其次,我的虚拟塞特犬甚至如何工作?

__iadd__ returns a replacement object to be rebound to the variable. __iadd__将替换对象返回到该变量。 This of course requires a setter. 当然,这需要二传手。

In this case it works because you're ignoring the set, but still leaving the original object in place, which you've changed in place. 在这种情况下,它起作用是因为您忽略了该集合,但仍将原始对象保留在原位置,而您将其更改了。

This behavior is required because some objects are immutable, but in place add still works on them. 因为某些对象是不可变的,所以必须执行此行为,但是在适当的位置添加仍然可以对它们起作用。

i += 5 takes the number i is bound to, adds 5 to it, and rebinds i to the NEW result number. i += 5接受与我绑定的数字,将其加5,然后将i重新绑定到新的结果数字。 That is, it is exactly equivalent to i = i + 5 , which has an assignment in it. 也就是说,它完全等于i = i + 5 ,其中有一个赋值。

It's caused by how Python's augmented assignment operators work. 这是由Python的扩展赋值运算符的工作方式引起的。 After calling the appropriate special method , they assign the return value to the object at the left hand side of the operator. 调用适当的特殊方法后 ,它们将返回值分配给运算符左侧的对象。

If x is an instance of a class with an __iadd__() method, x += y is equivalent to x = x.__iadd__(y) . 如果x是具有__iadd__()方法的类的实例,则x += y等效于x = x.__iadd__(y) Otherwise, x.__add__(y) and y.__radd__(x) are considered, as with the evaluation of x + y . 否则,将x.__add__(y)y.__radd__(x)视为与x + y的求值相同。

Therefore 因此

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

is equivalent to 相当于

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

and you need a setter to perform assignment. 并且您需要一个二传手来执行分配。 It doesn't have to do anything, though, because the __iadd__ method is defined and modifies the list in-place. 但是,它不需要执行任何操作,因为__iadd__方法已定义并就地修改了列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM