Python “for”循环中的 i = i + 1 和 i += 1 有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41446833/
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-20 00:58:39  来源:igfitidea点击:

What is the difference between i = i + 1 and i += 1 in a 'for' loop?

pythonloopsnumpyoperators

提问by Adam Fjeldsted

I found out a curious thing today and was wondering if somebody could shed some light into what the difference is here?

我今天发现了一件奇怪的事情,想知道是否有人可以阐明这里的区别?

import numpy as np

A = np.arange(12).reshape(4,3)
for a in A:
    a = a + 1

B = np.arange(12).reshape(4,3)
for b in B:
    b += 1

After running each forloop, Ahas not changed, but Bhas had one added to each element. I actually use the Bversion to write to a initialized NumPy array within a forloop.

每次for循环运行后,A都没有改变,只是B每个元素都加了一个。我实际上使用该B版本在for循环中写入初始化的 NumPy 数组。

回答by MSeifert

The difference is that one modifies the data-structure itself (in-place operation) b += 1while the other just reassignsthe variable a = a + 1.

不同之处在于,一个修改数据结构本身(就地操作),b += 1而另一个只是重新分配变量a = a + 1



Just for completeness:

只是为了完整性:

x += yis not alwaysdoing an in-place operation, there are (at least) three exceptions:

x += y不是总是做就地操作,有(至少)三种例外情况:

  • If xdoesn't implementan __iadd__method then the x += ystatement is just a shorthand for x = x + y. This would be the case if xwas something like an int.

  • If __iadd__returns NotImplemented, Python falls back to x = x + y.

  • The __iadd__method could theoretically be implemented to not work in place. It'd be really weird to do that, though.

  • 如果x没有实现__iadd__则方法的x += y声明仅仅是一个速记x = x + y。如果这会出现这种情况x是类似的int

  • 如果__iadd__返回NotImplemented,Python 回退到x = x + y.

  • __iadd__方法理论上可以实施到不工作。不过这样做真的很奇怪。

As it happens your bs are numpy.ndarrays which implements __iadd__and return itself so your second loop modifies the original array in-place.

碰巧您的bs 是numpy.ndarrays ,它实现__iadd__并返回自身,因此您的第二个循环就地修改了原始数组。

You can read more on this in the Python documentation of "Emulating Numeric Types".

您可以在Python 文档“模拟数字类型”中阅读更多相关信息。

These [__i*__] methods are called to implement the augmented arithmetic assignments (+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=). These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self). If a specific method is not defined, the augmented assignment falls back to the normal methods. For instance, if x is an instance of a class with an __iadd__()method, x += yis equivalent to x = x.__iadd__(y). Otherwise, x.__add__(y)and y.__radd__(x)are considered, as with the evaluation of x + y. In certain situations, augmented assignment can result in unexpected errors (see Why does a_tuple[i] += ["item"]raise an exception when the addition works?), but this behavior is in fact part of the data model.

__i*__调用这些 [ ] 方法来实现增强算术赋值(+=-=*=@=/=//=%=**=<<=>>=&=^=|=)。这些方法应该尝试就地执行操作(修改 self)并返回结果(可以是但不一定是 self)。如果未定义特定方法,则增广赋值回退到正常方法。例如,如果 x 是具有__iadd__()方法的类的实例,x += y则等效于x = x.__iadd__(y)。否则,x.__add__(y)y.__radd__(x)被考虑,与 的评估一样x + y。在某些情况下,扩充分配可能会导致意外错误(请参阅为什么a_tuple[i] += ["item"]添加工作时会引发异常?),但这种行为实际上是数据模型的一部分。

回答by Maroun

In the first example, you are reassigning the variable a, while in the second one you are modifying the data in-place, using the +=operator.

在第一个示例中,您正在重新分配变量a,而在第二个示例中,您正在使用+=运算符就地修改数据。

See the section about 7.2.1. Augmented assignment statements :

请参阅有关7.2.1的部分增强赋值语句

An augmented assignment expression like x += 1can be rewritten as x = x + 1to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead.

x += 1可以重写x = x + 1类似的增强赋值表达式以实现类似但不完全相同的效果。在增强版本中,x 只计算一次。此外,在可能的情况下,实际操作是就地执行的,这意味着不是创建新对象并将其分配给目标,而是修改旧对象。

+=operator calls __iadd__. This function makes the change in-place, and only after its execution, the result is set back to the object you are "applying" the +=on.

+=接线员调用__iadd__。此函数进行就地更改,只有在执行后,结果才会设置回您正在“应用”的对象+=

__add__on the other hand takes the parameters and returns their sum (without modifying them).

__add__另一方面,获取参数并返回它们的总和(不修改它们)。

回答by jmd_dk

As already pointed out, b += 1updates bin-place, while a = a + 1computes a + 1and then assigns the name ato the result (now adoes not refer to a row of Aanymore).

正如已经指出的那样,就地b += 1更新b,而a = a + 1计算a + 1然后将名称分配a给结果(现在a不再引用一行A)。

To understand the +=operator properly though, we need also to understand the concept of mutableversus immutableobjects. Consider what happens when we leave out the .reshape:

+=不过,要正确理解运算符,我们还需要了解可变对象与不可变对象的概念。考虑一下当我们省略 时会发生什么.reshape

C = np.arange(12)
for c in C:
    c += 1
print(C)  # [ 0  1  2  3  4  5  6  7  8  9 10 11]

We see that Cis notupdated, meaning that c += 1and c = c + 1are equivalent. This is because now Cis a 1D array (C.ndim == 1), and so when iterating over C, each integer element is pulled out and assigned to c.

我们看到,C没有更新,这意味着c += 1c = c + 1是等价的。这是因为 nowC是一个一维数组 ( C.ndim == 1),因此在迭代 时C,每个整数元素都会被拉出并分配给c

Now in Python, integers are immutable, meaning that in-place updates are not allowed, effectively transforming c += 1into c = c + 1, where cnow refers to a newinteger, not coupled to Cin any way. When you loop over the reshaped arrays, whole rows (np.ndarray's) are assigned to b(and a) at a time, which are mutableobjects, meaning that you are allowed to stick in new integers at will, which happens when you do a += 1.

现在在 Python 中,整数是不可变的,这意味着不允许就地更新,有效地转换c += 1c = c + 1,其中cnow 指的是一个整数,不以C任何方式耦合。当您循环重整数组时,整行 ( np.ndarray's) 一次被分配给b(and a),它们是可变对象,这意味着您可以随意插入新整数,这在您执行a += 1.

It should be mentioned that though +and +=are meant to be related as described above (and very much usually are), any type can implement them any way it wants by defining the __add__and __iadd__methods, respectively.

应该提到的是,尽管++=如上所述(并且通常是)相关联,但任何类型都可以通过分别定义__add____iadd__方法来以任何方式实现它们。

回答by Inconnu

The short form(a += 1) has the option to modify ain-place , instead of creating a new object representing the sum and rebinding it back to the same name(a = a + 1).So,The short form(a += 1) is much efficient as it doesn't necessarily need to make a copy of aunlike a = a + 1.

短格式( a += 1) 可以选择a就地修改,而不是创建一个表示总和的新对象并将其重新绑定回相同的名称( a = a + 1)。因此,短格式( a += 1) 非常有效,因为它不一定需要制作一个a不同的副本a = a + 1

Also even if they are outputting the same result, notice they are different because they are separate operators: +and +=

此外,即使它们输出相同的结果,请注意它们是不同的,因为它们是单独的运算符:++=

回答by Andi Kleve

First off: The variables a and b in the loops refer to numpy.ndarrayobjects.

首先:循环中的变量 a 和 b 指的是numpy.ndarray对象。

In the first loop, a = a + 1is evaluated as follows: the __add__(self, other)function of numpy.ndarrayis called. This creates a new object and hence, A is not modified. Afterwards, the variable ais set to refer to the result.

在第一个循环中,a = a + 1评估如下:调用的__add__(self, other)函数numpy.ndarray。这会创建一个新对象,因此不会修改 A。之后,变量a被设置为引用结果。

In the second loop, no new object is created. The statement b += 1calls the __iadd__(self, other)function of numpy.ndarraywhich modifies the ndarrayobject in place to which b is referring to. Hence, Bis modified.

在第二个循环中,没有创建新对象。该语句b += 1调用which的__iadd__(self, other)函数numpy.ndarray修改ndarrayb 所指的对象。因此,B被修改。

回答by hpaulj

A key issue here is that this loop iterates over the rows (1st dimension) of B:

这里的一个关键问题是此循环迭代 的行(第一维)B

In [258]: B
Out[258]: 
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
In [259]: for b in B:
     ...:     print(b,'=>',end='')
     ...:     b += 1
     ...:     print(b)
     ...:     
[0 1 2] =>[1 2 3]
[3 4 5] =>[4 5 6]
[6 7 8] =>[7 8 9]
[ 9 10 11] =>[10 11 12]

Thus the +=is acting on a mutable object, an array.

因此+=作用于一个可变对象,一个数组。

This is implied in the other answers, but easily missed if your focus is on the a = a+1reassignment.

这在其他答案中有所暗示,但如果您的重点是a = a+1重新分配,则很容易错过。

I could also make an in-place change to bwith [:]indexing, or even something fancier, b[1:]=0:

我也可以做一个就地变化b[:]索引,甚至一些票友,b[1:]=0

In [260]: for b in B:
     ...:     print(b,'=>',end='')
     ...:     b[:] = b * 2

[1 2 3] =>[2 4 6]
[4 5 6] =>[ 8 10 12]
[7 8 9] =>[14 16 18]
[10 11 12] =>[20 22 24]

Of course with a 2d array like Bwe usually don't need to iterate on the rows. Many operations that work on a single of Balso work on the whole thing. B += 1, B[1:] = 0, etc.

当然,对于二维数组,B我们通常不需要对行进行迭代。许多对单个B事物起作用的操作也对整个事物起作用。 B += 1B[1:] = 0等等。