Python 中 a -= b 和 a = a - b 的区别
Difference between a -= b and a = a - b in Python
我最近应用了 解决方案来平均每 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 阵列?
在内部,区别在于:
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__()
时改变 return 自身,而它们应该 return 一个带有 __sub__()
.
的新对象
对 numpy 对象应用切片操作会在它们上创建视图,因此使用它们会直接访问 "original" 对象的内存。
注意:在 NumPy 数组上使用 in-place 操作共享内存在 1.13.0 及更高版本中不再是问题(详见 here)。这两个操作将产生相同的结果。此答案仅适用于早期版本的 NumPy。
在计算中使用数组时改变数组可能会导致意外结果!
在问题的例子中,-=
的减法修改了 a
的第二个元素,然后立即在操作中使用 修改的 第二个元素在 a
.
的第三个元素上
下面是 a[1:] -= a[:-1]
一步一步发生的事情:
a
是包含数据 [1, 2, 3]
.
的数组
我们对这个数据有两种看法:a[1:]
是[2, 3]
,a[:-1]
是[1, 2]
。
in-place减法-=
开始。 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]
先创建了一个new数组,然后把这个数组中的值赋值给b[1:]
.它在减法期间不会修改 b
本身,因此视图 b[1:]
和 b[:-1]
不会更改。
一般建议是避免在一个视图与另一个视图重叠时就地修改它们。这包括运算符 -=
、*=
等,并在通用函数(如 np.subtract
和 np.multiply
)中使用 out
参数写回其中一个数组。
The docs 说:
The idea behind augmented assignment in Python is that it isn't
just an easier way to write the common practice of storing the
result of a binary operation in its left-hand operand, but also a
way for the left-hand operand in question to know that it should
operate `on itself', rather than creating a modified copy of
itself.
作为经验法则,增减法(x-=y
)是x.__isub__(y)
,对于IN-place操作IF 可能,当正常减法 (x = x-y
) 为 x=x.__sub__(y)
时。在像整数这样的非可变对象上,它是等价的。但是对于像数组或列表这样的可变对象,就像你的例子一样,它们可能是非常不同的东西。
我最近应用了 -=
运算符时。
举个小例子:
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 阵列?
在内部,区别在于:
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__()
时改变 return 自身,而它们应该 return 一个带有 __sub__()
.
对 numpy 对象应用切片操作会在它们上创建视图,因此使用它们会直接访问 "original" 对象的内存。
注意:在 NumPy 数组上使用 in-place 操作共享内存在 1.13.0 及更高版本中不再是问题(详见 here)。这两个操作将产生相同的结果。此答案仅适用于早期版本的 NumPy。
在计算中使用数组时改变数组可能会导致意外结果!
在问题的例子中,-=
的减法修改了 a
的第二个元素,然后立即在操作中使用 修改的 第二个元素在 a
.
下面是 a[1:] -= a[:-1]
一步一步发生的事情:
a
是包含数据[1, 2, 3]
. 的数组
我们对这个数据有两种看法:
a[1:]
是[2, 3]
,a[:-1]
是[1, 2]
。in-place减法
-=
开始。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]
先创建了一个new数组,然后把这个数组中的值赋值给b[1:]
.它在减法期间不会修改 b
本身,因此视图 b[1:]
和 b[:-1]
不会更改。
一般建议是避免在一个视图与另一个视图重叠时就地修改它们。这包括运算符 -=
、*=
等,并在通用函数(如 np.subtract
和 np.multiply
)中使用 out
参数写回其中一个数组。
The docs 说:
The idea behind augmented assignment in Python is that it isn't just an easier way to write the common practice of storing the result of a binary operation in its left-hand operand, but also a way for the left-hand operand in question to know that it should operate `on itself', rather than creating a modified copy of itself.
作为经验法则,增减法(x-=y
)是x.__isub__(y)
,对于IN-place操作IF 可能,当正常减法 (x = x-y
) 为 x=x.__sub__(y)
时。在像整数这样的非可变对象上,它是等价的。但是对于像数组或列表这样的可变对象,就像你的例子一样,它们可能是非常不同的东西。