Python中a – = b和a = a – b之间的区别

我最近应用这个解决scheme来平均每N行matrix。 虽然解决scheme通常在应用于7x1arrays时遇到问题。 我注意到,问题是使用-=运算符。 举一个小例子:

 import numpy as np a = np.array([1,2,3]) b = np.copy(a) a[1:] -= a[:-1] b[1:] = b[1:] - b[:-1] print a print b 

其输出:

 [1 1 2] [1 1 1] 

所以,在数组的情况下, a -= b产生一个不同的结果比a = a - b 。 我直到现在才想到这两种方式是完全一样的。 有什么不同?

为什么总结matrix中的每个N行的方法,例如对于一个7×4的matrix,而不是对一个7×1的arrays来说呢?

注意:在版本1.13.0以上的版本中,对共享内存的NumPyarrays使用就地操作不再有问题(请参见此处的详细信息)。 这两个操作将产生相同的结果。 这个答案只适用于早期版本的NumPy。


在计算中使用它们时突变数组可能会导致意想不到的结果!

在问题的例子中,用-=减法修改a的第二个元素,然后立即在a的第三个元素的操作中使用修改的第二个元素。

以下是a[1:] -= a[:-1]一步一步发生的情况:

  • a是数据[1, 2, 3]的数组。

  • 我们对这个数据有两种观点: a[1:][2, 3]a[:-1][1, 2]

  • 就地减法-=开始。 从a[1:]的第一个元素中减去a[:-1]的第一个元素, a[1:] 。 这已经修改为[1, 1, 3] 。 现在我们已经知道a[1:]是数据[1, 3]的视图,而a[:-1]是数据[1, 1] a[:-1]的视图(数组a的第二个元素已被更改) 。

  • a[:-1]现在是[1, 1]而NumPy现在必须从a[1:]的第二个元素中减去其第二个元素,即1 (不再是2!)。 这使a[1:]成为值[1, 2] a[1:]的视图。

  • a现在是一个值为[1, 1, 2]的数组。

b[1:] = b[1:] - b[:-1]没有这个问题,因为b[1:] - b[:-1]创build一个新的数组,然后赋值给这个数组b[1:] 。 它在减法期间不会修改b本身,所以视图b[1:]b[:-1]不会改变。


一般的build议是避免修改一个视图与另一个视图重叠。 这包括运算符-=*=等,并在通用函数(如np.subtractnp.multiply )中使用out参数写回到其中一个数组。

在内部,区别在于:

 a[1:] -= a[:-1] 

相当于这个:

 a[1:] = a[1:].__isub__(a[:-1]) a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None))) 

而这个:

 b[1:] = b[1:] - b[:-1] 

映射到这个:

 b[1:] = b[1:].__sub__(b[:-1]) b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None))) 

在某些情况下, __sub__()__isub__()以相似的方式工作。 但是当使用__isub__() ,可变对象应该进行变异和返回,而它们应该用__isub__()返回一个新对象。

在numpy对象上应用slice操作可以在它们上创build视图,所以直接使用它们可以访问“原始”对象的内存。

文档说:

Python中增强赋值背后的想法是,它不仅仅是一种更简单的方法来编写将二进制运算的结果存储在其左侧操作数中的常见做法,而且也是一种左侧操作数有问题的方式知道它应该“自行”运行,而不是创build自己的修改副本。

作为拇指规则,当正常减法( x = xy )是x=x.__sub__(y)时,扩大的减法( x-=y )是x.__isub__(y) ,对于IN置换运算。 对于像整数这样的非可变对象,它是等价的。 但是对于像数组或列表这样的可变元素,它们可能是完全不同的东西。