Python 中的“i += x”何时与“i = i + x”不同?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15376509/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 19:58:08  来源:igfitidea点击:

When is "i += x" different from "i = i + x" in Python?

pythonoperators

提问by MarJamRob

I was told that +=can have different effects than the standard notation of i = i +. Is there a case in which i += 1would be different from i = i + 1?

有人告诉我,它的+=效果可能与i = i +. 是否存在与i += 1不同的情况i = i + 1

采纳答案by mgilson

This depends entirely on the object i.

这完全取决于对象i

+=calls the __iadd__method(if it exists -- falling back on __add__if it doesn't exist) whereas +calls the __add__method1or the __radd__method in a few cases2.

+=调用__iadd__方法(如果它存在——__add__如果它不存在则回退),而在少数情况下+调用__add__方法1__radd__方法2

From an API perspective, __iadd__is supposed to be used for modifying mutable objects in place(returning the object which was mutated) whereas __add__should return a new instanceof something. For immutableobjects, both methods return a new instance, but __iadd__will put the new instance in the current namespace with the same name that the old instance had. This is why

从 API 的角度来看,__iadd__应该用于就地修改可变对象(返回发生变异的对象),而__add__应该返回某物的新实例。对于不可变对象,这两种方法都返回一个新实例,但__iadd__会将新实例放入与旧实例同名的当前命名空间中。这就是为什么

i = 1
i += 1

seems to increment i. In reality, you get a new integer and assign it "on top of" i-- losing one reference to the old integer. In this case, i += 1is exactly the same as i = i + 1. But, with most mutable objects, it's a different story:

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

As a concrete example:

作为一个具体的例子:

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]

compared to:

相比:

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

notice how in the first example, since band areference the same object, when I use +=on b, it actually changes b(and asees that change too -- After all, it's referencing the same list). In the second case however, when I do b = b + [1, 2, 3], this takes the list that bis referencing and concatenates it with a new list [1, 2, 3]. It then stores the concatenated list in the current namespace as b-- With no regard for what bwas the line before.

请注意在第一个示例中,因为ba引用同一个对象,当我使用+=on 时b,它实际上发生了变化b(并且也a看到了这种变化——毕竟,它引用的是同一个列表)。然而,在第二种情况下,当我这样做时b = b + [1, 2, 3],这会获取b正在引用的列表并将其与一个新列表连接起来[1, 2, 3]。然后它将串联列表存储在当前命名空间中作为b-- 不考虑b之前的行是什么。



1In the expression x + y, if x.__add__isn't implemented or if x.__add__(y)returns NotImplementedandxand yhave different types, then x + ytries to call y.__radd__(x). So, in the case where you have

1在该表达式中x + y,如果x.__add__未实现,或者如果x.__add__(y)返回NotImplementedxy具有不同类型,然后x + y尝试呼叫y.__radd__(x)。所以,如果你有

foo_instance += bar_instance

foo_instance += bar_instance

if Foodoesn't implement __add__or __iadd__then the result here is the same as

如果Foo没有实现__add__或者__iadd__那么这里的结果是一样的

foo_instance = bar_instance.__radd__(bar_instance, foo_instance)

foo_instance = bar_instance.__radd__(bar_instance, foo_instance)

2In the expression foo_instance + bar_instance, bar_instance.__radd__will be tried before foo_instance.__add__ifthe type of bar_instanceis a subclass of the type of foo_instance(e.g. issubclass(Bar, Foo)). The rational for this is because Baris in some sense a "higher-level" object than Fooso Barshould get the option of overriding Foo's behavior.

2在表达式中foo_instance + bar_instance如果类型是类型的子类(例如),bar_instance.__radd__将在之前尝试。合理的,这是因为在某种意义上是一种“更高级”的对象不是那么应该得到压倒一切的选项的行为。foo_instance.__add__bar_instancefoo_instanceissubclass(Bar, Foo)BarFooBarFoo

回答by abarnert

Under the covers, i += 1does something like this:

在幕后,i += 1做这样的事情:

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

While i = i + 1does something like this:

虽然i = i + 1做了这样的事情:

i = i.__add__(1)

This is a slight oversimplification, but you get the idea: Python gives types a way to handle +=specially, by creating an __iadd__method as well as an __add__.

这有点过于简单化了,但你明白了:Python+=通过创建一个__iadd__方法和一个__add__.

The intention is that mutable types, like list, will mutate themselves in __iadd__(and then return self, unless you're doing something very tricky), while immutable types, like int, will just not implement it.

目的是可变类型,如list,会在__iadd__(然后返回self,除非你做一些非常棘手的事情),而不可变类型,如,int不会实现它。

For example:

例如:

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

Because l2is the same object as l1, and you mutated l1, you also mutated l2.

因为l2是同一个对象l1,你变异了l1,你也变异了l2

But:

但:

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

Here, you didn't mutate l1; instead, you created a new list, l1 + [3], and rebound the name l1to point at it, leaving l2pointing at the original list.

在这里,你没有变异l1;相反,您创建了一个新列表,l1 + [3]并重新调用名称l1以指向它,而将l2指向原始列表。

(In the +=version, you were also rebinding l1, it's just that in that case you were rebinding it to the same listit was already bound to, so you can usually ignore that part.)

(在该+=版本中,您也重新绑定了l1,只是在这种情况下,您将它重新绑定到list它已经绑定到的相同位置,因此您通常可以忽略该部分。)

回答by Deqing

Here is an example that directly compares i += xwith i = i + x:

下面是比较直接的例子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]