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


90

我最近将这种解决方案应用于平均每N行矩阵。尽管该解决方案总体上可行,但将其应用于7x1阵列时遇到了问题。我注意到问题出在使用-=运算符时。举一个小例子:

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。我认为到目前为止,这两种方式是完全相同的。有什么区别?

我提到的用于对矩阵中的每N行求和的方法如何工作(例如,对于7x4矩阵而不是7x1数组)?

Answers:


80

注意:从1.13.0版开始,在共享内存的NumPy数组上使用就地操作不再是问题(请参阅此处的详细信息)。这两个操作将产生相同的结果。此答案仅适用于NumPy的早期版本。


在计算中使用数组时对其进行突变可能会导致意外结果!

在问题的示例中,用减去可-=修改的第二个元素,a然后立即在对的第三个元素的运算中使用该修改的第二个元素a

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

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

  • 我们对此数据有两种看法:a[1:]is [2, 3]a[:-1]is [1, 2]

  • 就地减法-=开始。的第一个元素a[:-1]1减去的第一个元素a[1:]。这已修改a[1, 1, 3]。现在,我们有一个a[1:]是数据的视图[1, 3],并且a[:-1]是数据的视图[1, 1](阵列的第二元件a已被更改)。

  • a[:-1]现在是[1, 1],NumPy现在必须从的第二个元素减去其第二个元素1(现在不再 2!)a[1:]。这样就a[1:]可以看到这些值[1, 2]

  • a现在是具有值的数组[1, 1, 2]

b[1:] = b[1:] - b[:-1]不会出现此问题,因为首先b[1:] - b[:-1]创建一个数组,然后将该数组中的值分配给b[1:]b在减法期间它不会自行修改,因此视图b[1:]b[:-1]不会更改。


一般建议是,如果一个视图重叠,则应避免在一个视图之间进行修改。这包括运算符-=*=等,并out在通用函数(如np.subtractnp.multiply)中使用参数写回数组之一。


4
与当前接受的答案相比,我更喜欢此答案。它使用非常清晰的语言来显示在适当位置修改可变对象的效果。更重要的是,最后一段直接强调了对重叠视图进行就地修改的重要性,这应该是从这个问题中吸取的教训。
Reti43 '16

43

在内部,区别在于:

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__(),而它们应该使用来返回一个新对象__sub__()

在numpy对象上应用切片操作会在其上创建视图,因此使用它们直接访问“原始”对象的内存。


11

文档说:

Python中的增强赋值背后的想法是,它不仅是编写将二进制运算的结果存储在其左操作数中的通用做法的简便方法,而且还是一种用于所讨论的左操作数的方法。知道它应该“自己”操作,而不是创建自己的修改副本。

作为一个经验法则,增强减法(x-=y)是x.__isub__(y),对于IN -place操作 IF可能的,当正常减法(x = x-y)是x=x.__sub__(y)。在像整数这样的非可变对象上,它是等效的。但是对于像数组或列表这样的可变变量,如您的示例所示,它们可能是完全不同的东西。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.