在Python中,“i += x”与“i = i + x”不同?

时间:2022-09-06 16:32:34

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

I was told that += can have different effects than the standard notation of I = I +。i += 1的情况是否与i + 1不同?

4 个解决方案

#1


280  

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__ method1 or the __radd__ method in a few cases2.

+=调用__iadd__方法(如果它存在的话——如果它不存在,则返回__add__),而+在一些cases2中调用__add__ method1或__radd__方法。

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 instance of something. For immutable objects, 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 += 1 is exactly the same as i = i + 1. But, with most mutable objects, it's a different story:

在现实中,你得到一个新的整数,并将它赋值给“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 b and a reference the same object, when I use += on b, it actually changes b (and a sees 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 b is 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 b was the line before.

注意,在第一个示例中,由于b和一个引用相同的对象,当我使用+= on b时,它实际上改变了b(而且a也看到了变化——毕竟,它引用了相同的列表)。在第二种情况下,当我做b = b +[1, 2, 3]时,这个列表会列出b正在引用的列表,并将其与一个新的列表连接起来[1,2,3]。然后,它将当前名称空间中的连接列表存储为b,而不考虑之前的行。


1In the expression x + y, if x.__add__ isn't implemented or if x.__add__(y) returns NotImplemented and x and y have different types, then x + y tries to call y.__radd__(x). So, in the case where you have

在表达式x + y中,如果x。__add__没有实现,或者如果x.__add__(y)返回notimplem, x和y有不同的类型,那么x + y尝试调用。__radd__(x)。所以,在这个例子中。

foo_instance += bar_instance

foo_instance + = bar_instance

if Foo doesn'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__ if the type of bar_instance is a subclass of the type of foo_instance (e.g. issubclass(Bar, Foo)). The rational for this is because Bar is in some sense a "higher-level" object than Foo so Bar should get the option of overriding Foo's behavior.

在表达式foo_instance + bar_instance, bar_instance。__radd__将在foo_instance之前尝试。__add__如果bar_instance类型是foo_instance类型的子类(例如issubclass(Bar, Foo))。这是因为Bar在某种意义上是一个比Foo更高级的对象,所以Bar应该可以选择重写Foo的行为。

#2


63  

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

在封面下,i += 1是这样的:

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

While i = i + 1 does 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通过创建一个__add__方法和一个__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 l2 is 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 l1 to point at it, leaving l2 pointing 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 list it was already bound to, so you can usually ignore that part.)

(在+=版本中,您也重新绑定l1,只是在这种情况下,您将它重新绑定到它已经绑定到的同一个列表中,所以您通常可以忽略该部分)。

#3


5  

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

这里有一个例子,直接比较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]

#4


2  

If you're just dealing with literals, then i += 1 has the same behavior as i = i + 1.

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

#1


280  

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__ method1 or the __radd__ method in a few cases2.

+=调用__iadd__方法(如果它存在的话——如果它不存在,则返回__add__),而+在一些cases2中调用__add__ method1或__radd__方法。

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 instance of something. For immutable objects, 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 += 1 is exactly the same as i = i + 1. But, with most mutable objects, it's a different story:

在现实中,你得到一个新的整数,并将它赋值给“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 b and a reference the same object, when I use += on b, it actually changes b (and a sees 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 b is 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 b was the line before.

注意,在第一个示例中,由于b和一个引用相同的对象,当我使用+= on b时,它实际上改变了b(而且a也看到了变化——毕竟,它引用了相同的列表)。在第二种情况下,当我做b = b +[1, 2, 3]时,这个列表会列出b正在引用的列表,并将其与一个新的列表连接起来[1,2,3]。然后,它将当前名称空间中的连接列表存储为b,而不考虑之前的行。


1In the expression x + y, if x.__add__ isn't implemented or if x.__add__(y) returns NotImplemented and x and y have different types, then x + y tries to call y.__radd__(x). So, in the case where you have

在表达式x + y中,如果x。__add__没有实现,或者如果x.__add__(y)返回notimplem, x和y有不同的类型,那么x + y尝试调用。__radd__(x)。所以,在这个例子中。

foo_instance += bar_instance

foo_instance + = bar_instance

if Foo doesn'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__ if the type of bar_instance is a subclass of the type of foo_instance (e.g. issubclass(Bar, Foo)). The rational for this is because Bar is in some sense a "higher-level" object than Foo so Bar should get the option of overriding Foo's behavior.

在表达式foo_instance + bar_instance, bar_instance。__radd__将在foo_instance之前尝试。__add__如果bar_instance类型是foo_instance类型的子类(例如issubclass(Bar, Foo))。这是因为Bar在某种意义上是一个比Foo更高级的对象,所以Bar应该可以选择重写Foo的行为。

#2


63  

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

在封面下,i += 1是这样的:

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

While i = i + 1 does 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通过创建一个__add__方法和一个__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 l2 is 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 l1 to point at it, leaving l2 pointing 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 list it was already bound to, so you can usually ignore that part.)

(在+=版本中,您也重新绑定l1,只是在这种情况下,您将它重新绑定到它已经绑定到的同一个列表中,所以您通常可以忽略该部分)。

#3


5  

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

这里有一个例子,直接比较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]

#4


2  

If you're just dealing with literals, then i += 1 has the same behavior as i = i + 1.

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