我发现numpy
数组在多个位置通过引用传递,但是当我执行以下代码时,为什么foo
和{{1}的行为之间存在差异? }}
bar
我正在使用python 2.7和numpy版本1.6.1
答案 0 :(得分:62)
在Python中,all variable names are references to values。
当Python评估作业时,the right-hand side is evaluated before the left-hand side。 arr - 3
创建一个新数组;它不会就地修改arr
。
arr = arr - 3
使局部变量arr
引用此新数组。它不会修改传递给arr
的{{1}}最初引用的值。变量名foo
只是绑定到新数组arr
。此外,arr - 3
是arr
函数范围内的局部变量名。一旦foo
函数完成,就不再有foo
的引用,Python可以自由地收集它引用的值。 As Reti43 points out,要使arr
的值影响arr
,a
必须返回foo
,并且arr
必须分配给该值:
a
相比之下,Python def foo(arr):
arr = arr - 3
return arr
# or simply combine both lines into `return arr - 3`
a = foo(a)
转换为对__iadd__
special method的调用,确实会修改arr -= 3
引用的数组。
答案 1 :(得分:8)
第一个函数计算(arr - 3)
,然后为其分配本地名称arr
,这不会影响传入的数组数据。我的猜测是在第二个函数{{1}覆盖np.array
运算符,并在数组数据上运行。
答案 2 :(得分:2)
Python通过引用传递数组:
$:python
...python startup message
>>> import numpy as np
>>> x = np.zeros((2,2))
>>> x
array([[0.,0.],[0.,0.]])
>>> def setx(x):
... x[0,0] = 1
...
>>> setx(x)
>>> x
array([[1.,0.],[0.,0.]])
最重要的答案是指即使在已编译的c代码中也会发生的现象,因为任何BLAS事件都将涉及“读入”步骤,在此步骤中,将形成一个由用户(在这种情况下为代码编写者)组成的新数组知道,或者在用户不知道的临时变量中“在幕后”形成了一个新数组(您可能将其视为.eval()
调用)。
但是,我可以清楚地访问数组的内存,就好像它位于比调用的函数(即setx(...)
)更全局的范围内;就编写代码而言,这正是“通过引用传递”的含义。
让我们再做一些测试来检查已接受答案的有效性:
(continuing the session above)
>>> def minus2(x):
... x[:,:] -= 2
...
>>> minus2(x)
>>> x
array([[-1.,-2.],[-2.,-2.]])
似乎通过引用传递。让我们进行一次计算,该计算肯定会计算出一个底层的中间数组,并查看x是否像通过引用传递一样进行了修改:
>>> def pow2(x):
... x = x * x
...
>>> pow2(x)
>>> x
array([[-1.,-2.],[-2.,-2.]])
呵呵,我以为x是通过引用传递的,但也许不是吗? -不,在这里,我们用一个全新的声明(在python中通过解释将其隐藏)遮盖了x,并且python不会将此“阴影”传播回全局范围(这将违反python用例:即,成为一种入门级的编码语言,专家仍然可以有效地使用它。)
但是,通过强制修改内存(将x提交给函数时不会复制),我可以很容易地以“引用传递”的方式执行此操作:
>>> def refpow2(x):
... x *= x
...
>>> refpow2(x)
>>> x
array([[1., 4.],[4., 4.]])
因此,您会发现可以对python进行一些细化以完成您想做的事情。