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有相同的行为。