Python中,“i + = x”与“i = i + x”的区别是什么?
我被告知+=
可以有不同于i = i +
的标准符号的效果。 有没有i += 1
与i = i + 1
?
这完全取决于对象i
。
+=
调用__iadd__
方法 (如果它存在 – 在__add__
上__add__
如果它不存在),而在一些情况下 +
调用__add__
方法 1或__radd__
方法 2 。
从API的angular度来看, __iadd__
应该用于修改可变对象(返回被突变的对象),而__add__
应该返回一个新的事件实例 。 对于不可变的对象,这两个方法都会返回一个新的实例,但__iadd__
会将新实例放在当前名称空间中,并且与旧实例具有相同的名称。 这就是为什么
i = 1 i += 1
似乎增加i
。 实际上,你会得到一个新的整数,并将其分配给“在…之上” – 失去对旧整数的引用。 在这种情况下, i += 1
与i = 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]
注意在第一个例子中,因为b
和a
引用同一个对象,所以当我使用+=
on b
,它实际上改变了b
(并且也看到了改变 – 毕竟它引用了同样的列表)。 然而,在第二种情况下,当我做b = b + [1, 2, 3]
,这将采用b
引用的列表,并将其与新列表[1, 2, 3]
。 然后将连接列表作为b
存储在当前名称空间中 – 不考虑以前是哪一行。
1在expression式x + y
,如果未实现x.__add__
或者如果x.__add__(y)
返回NotImplemented
并且 x
和y
具有不同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_instance
types的子类(例如, 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]
因为l2
和l1
是同一个对象,所以你突变了l1
,你也突变了l2
。
但:
>>> l1 = [] >>> l2 = l1 >>> l1 = l1 + [3] >>> l2 []
在这里,你没有改变l1
; 相反,你创build了一个新的列表l1 + [3]
,并且将名字l1
反弹到指向它,留下l2
指向原始列表。
(在+=
版本中,你也正在重新绑定l1
,只是在这种情况下,你将它重新绑定到已绑定的list
中,所以通常可以忽略该部分。)
这里是一个直接比较i += x
和i = 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我在相同的内存位置。 这比以前更有效率。