¿Son las matrices numpy pasadas por referencia?

Me di cuenta de que numpy matrices numpy se pasan por referencia en varios lugares, pero cuando ejecuto el siguiente código, ¿por qué hay una diferencia entre el comportamiento de foo y bar

 import numpy as np def foo(arr): arr = arr - 3 def bar(arr): arr -= 3 a = np.array([3, 4, 5]) foo(a) print a # prints [3, 4, 5] bar(a) print a # prints [0, 1, 2] 

Estoy usando python 2.7 y numpy versión 1.6.1

En Python, todos los nombres de variables son referencias a valores .

Cuando Python evalúa una asignación, el lado derecho se evalúa antes que el lado izquierdo . arr - 3 crea una nueva matriz; no modifica arr en el lugar.

arr = arr - 3 hace que la variable local arr haga referencia a esta nueva matriz. No modifica el valor originalmente referenciado por arr que se pasó a foo . La variable nombre arr simplemente se enlaza a la nueva matriz, arr - 3 . Además, arr es el nombre de la variable local en el scope de la función foo . Una vez que se completa la función foo , no hay más referencias a arr y Python es libre de recolectar el valor al que hace referencia. Como señala Reti43 , para que el valor de arr afecte a , foo debe devolver arr y debe asignarse a ese valor:

 def foo(arr): arr = arr - 3 return arr # or simply combine both lines into `return arr - 3` a = foo(a) 

En contraste, arr -= 3 , que Python se traduce en una llamada al método especial __iadd__ , modifica la matriz a la que hace referencia arr en el lugar.

La primera función calcula (arr - 3) , luego le asigna el nombre local arr , lo que no afecta a los datos de matriz pasados. np.array que en la segunda función, np.array anula el operador -= y opera en su lugar en la matriz de datos.

Python pasa la matriz por referencia:

 $: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.]]) 

La respuesta principal se refiere a un fenómeno que ocurre incluso en el código C comstackdo, ya que cualquier evento BLAS implicará un paso de “lectura en” donde se formará una nueva matriz que el usuario (escritor de códigos en este caso) conoce , o se forma una nueva matriz “debajo del capó” en una variable temporal que el usuario desconoce (puede que vea esto como una llamada .eval() ).

Sin embargo, puedo acceder claramente a la memoria de la matriz como si estuviera en un ámbito más global que la función llamada (es decir, setx(...) ); que es exactamente lo que “pasar por referencia” es, en términos de escribir código.


Y hagamos algunas pruebas más para verificar la validez de la respuesta aceptada:

 (continuing the session above) >>> def minus2(x): ... x[:,:] -= 2 ... >>> minus2(x) >>> x array([[-1.,-2.],[-2.,-2.]]) 

Parece que se pasa por referencia. Hagamos un cálculo que definitivamente computará una matriz intermedia bajo el capó, y veamos si x se modifica como si se pasara por referencia:

 >>> def pow2(x): ... x = x * x ... >>> pow2(x) >>> x array([[-1.,-2.],[-2.,-2.]]) 

Huh, pensé que x fue pasado por referencia, pero tal vez no lo es? – No, aquí, hemos sombreado la x con una nueva statement (que se oculta mediante la interpretación en python), y python no propagará esta “sombra” al scope global (lo que violaría el caso de uso de python, que es un lenguaje de encoding de nivel principiante que, no obstante, puede ser utilizado de manera efectiva por un experto).

Sin embargo, puedo realizar esta operación muy fácilmente de manera “paso por referencia” forzando la memoria (que no se copia cuando envío x a la función) para que se modifique en su lugar:

 >>> def refpow2(x): ... x *= x ... >>> refpow2(x) >>> x array([[1., 4.],[4., 4.]]) 

Y así ves que la python puede ser finecida un poco para hacer lo que estás tratando de hacer.