'for'循环中i = i + 1和i + = 1之间有什么区别?

Ada*_*ted 110 python loops numpy operators

我今天发现了一件奇怪的事情,并想知道是否有人可以了解这里的差异是什么?

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
Run Code Online (Sandbox Code Playgroud)

运行每个for循环后,A没有更改,但B已经添加了一个元素.我实际上使用该B版本写入for循环内的初始化NumPy数组.

MSe*_*ert 112

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


只是为了完整性:

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

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

  • 如果__iadd__返回NotImplemented,Python会回归x = x + y.

  • __iadd__理论上可以实施该方法以使其不起作用.但是,做到这一点真的很奇怪.

碰巧你的bs是numpy.ndarray实现__iadd__并返回自己的,所以你的第二个循环就地修改了原始数组.

您可以在"模拟数字类型"Python文档中阅读更多相关内容.

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

  • 抄袭最热门评论的技术性较低的解释:这里的"a = a + 1"在功能上等同于"dummy = a + 1",因为等号左侧的"a"被重新分配给不再是"A"的观点.因为`+ =`不像`=`那样重新分配,而是修改'到位','b`仍然是'B`的视图,因此`B`增加,而'A`则没有. (2认同)

Mar*_*oun 28

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

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

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

+=操作员电话__iadd__.此函数进行就地更改,并且只有在执行后,结果才会被设置回您正在"应用"的对象+=.

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


jmd*_*_dk 13

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

为了+=正确理解运算符,我们还需要理解可变对象和不可变对象的概念.考虑一下当我们遗漏时会发生什么.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]
Run Code Online (Sandbox Code Playgroud)

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

现在在Python中,整数是不可变的,这意味着不允许就地更新,有效地转换c += 1c = c + 1,c现在指的是一个新的整数,不以C任何方式耦合.当你遍历重新整形的数组时,整个行(np.ndarray和/)一次被分配给b(和a)可变对象,这意味着你可以随意使用新的整数,当你这样做时就会发生这种情况a += 1.

应该说,虽然提到的++=是换货如上所述是相关的(并且非常一般都是这样),任何类型的可以实现他们就是了通过定义任何的方式__add____iadd__方法,分别.


Inc*_*nnu 5

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

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