虽然我不知道底层发生了什么,但是在NumPy数组和Python列表中执行就地操作将返回相同的引用,这可能会导致混淆的结果,当它们被传递到函数中时。
从Python开始
>>> a = [1, 2, 3]
>>> b = a
>>> a is b
True
>>> id(a[2])
12345
>>> id(b[2])
12345
...其中12345
是内存中a[2]
值的位置的唯一标识符,与b[2]
相同。
因此,a
和b
引用内存中的同一列表。现在尝试对列表中的一个项目进行原地加法操作。
>>> a[2] += 4
>>> a
[1, 2, 7]
>>> b
[1, 2, 7]
>>> a is b
True
>>> id(a[2])
67890
>>> id(b[2])
67890
因此,在列表中就地添加项目仅更改了索引为
2
的项的值,但
a
和
b
仍然引用相同的列表,尽管列表中的第三个项目被重新分配给新值
7
。重新分配解释了为什么如果
a = 4
和
b = a
是整数(或浮点数)而不是列表,则
a += 1
将导致
a
被重新分配,然后
b
和
a
将是不同的引用。但是,如果调用列表添加,例如:
a += [5]
对于引用相同列表的
a
和
b
,它不会重新分配
a
;它们都将被追加。
现在看看NumPy
>>> import numpy as np
>>> a = np.array([1, 2, 3], float)
>>> b = a
>>> a is b
True
这些都是相同的参考文献,Python中与列表相同的就地操作符似乎具有相同的效果:
>>> a += 4
>>> a
array([ 5., 6., 7.])
>>> b
array([ 5., 6., 7.])
在原地添加
ndarray
会更新引用。这与调用
numpy.add
不同,后者会在新引用中创建副本。
>>> a = a + 4
>>> a
array([ 9., 10., 11.])
>>> b
array([ 5., 6., 7.])
借用引用的原地操作
我认为这里存在的危险是,如果将引用传递到不同的作用域。
>>> def f(x):
... x += 4
... return x
参数参考
x
被传递到作用域
f
中,它不会进行复制并且实际上会改变该参考值,并将其传递回来。
>>> f(a)
array([ 13., 14., 15.])
>>> f(a)
array([ 17., 18., 19.])
>>> f(a)
array([ 21., 22., 23.])
>>> f(a)
array([ 25., 26., 27.])
同样适用于Python列表:
>>> def f(x, y):
... x += [y]
>>> a = [1, 2, 3]
>>> b = a
>>> f(a, 5)
>>> a
[1, 2, 3, 5]
>>> b
[1, 2, 3, 5]
我认为这种操作容易引起混淆,有时也很难调试。因此,我只会在当前作用域中使用原地操作符,并且会谨慎处理借用引用。
x
的最后一个值覆盖了所有其他值)。 - Henry Gomersallx
中所有值的总和。嗯... - Henry Gomersall