一般的答案是+=尝试调用__iadd__
特殊方法,如果该方法不可用,它将尝试使用__add__
替代方法。因此,问题在于这些特殊方法之间的差异。
的__iadd__
特殊方法是就地此外,这是它发生变异,它作用于对象。该__add__
特殊方法返回一个新的对象,也可用于标准+操作。
因此,当在+=已__iadd__
定义的对象上使用运算符时,该对象将被修改。否则,它将尝试使用纯文本__add__
并返回一个新对象。
这就是为什么对于诸如列表之类的可变类型会+=更改对象的值,而对于诸如元组,字符串和整数之类的不可变类型则会返回一个新对象(a += b等于a = a + b
)的原因。
对于类型的同时支持__iadd__
,并__add__
因此你必须要小心你使用哪一个。a += b
将调用__iadd__
和变异a
,而a = a + b
将创建一个新对象并将其分配给a
。他们是不一样的操作!
>>> a1 = a2 = [1, 2]
>>> b1 = b2 = [1, 2]
>>> a1 += [3] # Uses __iadd__, modifies a1 in-place
>>> b1 = b1 + [3] # Uses __add__, creates new list, assigns it to b1
>>> a2
[1, 2, 3] # a1 and a2 are still the same list
>>> b2
[1, 2] # whereas only b1 was changed
对于不可变的类型(没有__iadd__
)a += b,a = a + b
它们是等效的。这就是让你+=在不可变类型上使用的原因,这似乎是一个奇怪的设计决定,除非你考虑到否则无法+=在数字等不可变类型上使用!
对于一般情况,请参见Scott Griffith
的答案。但是,当像你一样处理列表时,+=
运算符是的简写someListObject.extend(iterableObject)
。请参阅extend()
的文档。
执行此操作时,foo += something
你要foo
在适当位置修改列表,因此无需更改名称foo指向的引用,而是直接更改列表对象。使用foo = foo + something
,你实际上是在创建一个新列表。
此示例代码将对其进行解释:
>>> l = []
>>> id(l)
13043192
>>> l += [3]
>>> id(l)
13043192
>>> l = l + [3]
>>> id(l)
13059216
请注意,当你将新列表重新分配给时,参考如何变化l。
与bar使用类变量(而不是实例变量)一样,就地修改将影响该类的所有实例。但是,当重新定义时self.bar
,该实例将具有一个单独的实例变量,self.bar
而不会影响其他类实例。