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

时间:2016-01-27 11:29:44

标签: python arrays numpy variable-assignment in-place

我最近应用this解决方案来平均每N行矩阵。 虽然解决方案通常起作用,但在应用于7x1阵列时遇到了问题。我注意到问题出在使用empty_rows = df['columnname'].isnull() 运算符时。 举一个小例子:

-=

输出:

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不同。直到现在我才想到这两种方式完全相同。有什么区别?

为什么我提到的用于求和矩阵中每N行的方法是有用的,例如对于7x4矩阵而不是7x1阵列?

3 个答案:

答案 0 :(得分:79)

注意:在版本1.13.0以后不再存在共享内存的NumPy阵列上使用就地操作(请参阅详细信息here)。这两个操作将产生相同的结果。此答案仅适用于早期版本的NumPy。

在计算中使用数组进行变异会导致意外结果!

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

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

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

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

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

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

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

[1, 1, 2]没有此问题,因为b[1:] = b[1:] - b[:-1]首先创建 new 数组,然后将此数组中的值分配给b[1:] - b[:-1]。在减法期间,它不会修改b[1:]本身,因此视图bb[1:]不会更改。

一般建议是避免将一个视图与另一个视图重叠,如果它们重叠。这包括运算符b[:-1]-=等,并使用通用函数中的*=参数(如outnp.subtract)来回写其中一个数组。

答案 1 :(得分: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对象应用切片操作会在它们上创建视图,因此使用它们可以直接访问"原始"的内存。对象

答案 2 :(得分:11)

The docs说:

  

Python中增强赋值背后的想法是它不是       只是一种更简单的方式来编写存储的常见做法       左手操作数中的二进制操作的结果,但也是a       有问题的左手操作数的方式,知道它应该       “自己”操作,而不是创建一个修改后的副本       本身。

作为一个拇指规则,增强减法(x-=y)为x.__isub__(y) IN - 场操作 IF 可能,正常减法时( x = x-y)是x=x.__sub__(y)。在像整数这样的非可变对象上它是等价的。但是对于像数组或列表这样的可变类,如在你的例子中,它们可能是非常不同的东西。