Python中,“i + = x”与“i = i + x”的区别是什么?

我被告知+=可以有不同于i = i +的标准符号的效果。 有没有i += 1i = i + 1

这完全取决于对象i

+=调用__iadd__方法 (如果它存在 – 在__add____add__如果它不存在),而在一些情况下 +调用__add__方法 1__radd__方法 2

从API的angular度来看, __iadd__应该用于修改可变对象(返回被突变的对象),而__add__应该返回一个新的事件实例 。 对于不可变的对象,这两个方法都会返回一个新的实例,但__iadd__会将新实例放在当前名称空间中,并且与旧实例具有相同的名称。 这就是为什么

 i = 1 i += 1 

似乎增加i 。 实际上,你会得到一个新的整数,并将其分配给“在…之上” – 失去对旧整数的引用。 在这种情况下, i += 1i = i + 1完全相同。 但是,对于大多数可变对象,这是一个不同的故事:

作为一个具体的例子:

 a = [1, 2, 3] b = a b += [1, 2, 3] print a #[1, 2, 3, 1, 2, 3] print b #[1, 2, 3, 1, 2, 3] 

相比:

 a = [1, 2, 3] b = a b = b + [1, 2, 3] print a #[1, 2, 3] print b #[1, 2, 3, 1, 2, 3] 

注意在第一个例子中,因为ba引用同一个对象,所以当我使用+= on b ,它实际上改变了b (并且也看到了改变 – 毕竟它引用了同样的列表)。 然而,在第二种情况下,当我做b = b + [1, 2, 3] ,这将采用b引用的列表,并将其与新列表[1, 2, 3] 。 然后将连接列表作为b存储在当前名称空间中 – 不考虑以前是哪一行。


1在expression式x + y ,如果未实现x.__add__或者如果x.__add__(y)返回NotImplemented 并且 xy具有不同types ,则x + y尝试调用y.__radd__(x)所以,如果你有

foo_instance += bar_instance

如果Foo没有实现__add____iadd__那么这里的结果是一样的

foo_instance = bar_instance.__radd__(bar_instance, foo_instance)

2在expression式foo_instance + bar_instance如果 foo_instance.__add__的types是bar_instancetypes的子类(例如, issubclass(Bar, Foo)issubclass(Bar, Foo)则会在foo_instance.__add__之前尝试foo_instance.__add__理性的是,因为Bar在某种意义上是比Foo更“高级”的对象,所以Bar应该可以select重写Foo的行为。

在封面下, i += 1做这样的事情:

 try: i = i.__iadd__(1) except AttributeError: i = i.__add__(1) 

i = i + 1做这样的事情:

 i = i.__add__(1) 

这只是一个简单的过度简化,但是你明白了:Python通过创build一个__iadd__方法以及一个__add__来为types提供一种处理+=的方法。

其目的是,可变types,如list ,会改变__iadd__自己(然后返回self ,除非你做一些非常棘手的事情),而不可变的types,如int ,将不会实现它。

例如:

 >>> l1 = [] >>> l2 = l1 >>> l1 += [3] >>> l2 [3] 

因为l2l1是同一个对象,所以你突变了l1 ,你也突变了l2

但:

 >>> l1 = [] >>> l2 = l1 >>> l1 = l1 + [3] >>> l2 [] 

在这里,你没有改变l1 ; 相反,你创build了一个新的列表l1 + [3] ,并且将名字l1反弹到指向它,留下l2指向原始列表。

(在+=版本中,你也正在重新绑定l1 ,只是在这种情况下,你将它重新绑定到已绑定的list中,所以通常可以忽略该部分。)

这里是一个直接比较i += xi = i + x的例子:

 def foo(x): x = x + [42] def bar(x): x += [42] c = [27] foo(c); # c is not changed bar(c); # c is changed to [27, 42] 

如果你只是处理文字,那么i += 1具有与i = i + 1相同的行为。

简而言之,你有两种情况:

 i = i + 1 

这将创build一个新的variablesi,使用前一个i的值并将其加1并将其存储在其他一些存储位置中。

 i += 1 

这不会创build一个新的variables,而是增加variables我在相同的内存位置。 这比以前更有效率。