J'ai découvert le fait que les tableaux numpy
sont passés par référence à plusieurs endroits, mais quand j'exécute le code suivant, pourquoi existe-t-il une différence entre le comportement de foo
et 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]
J'utilise python 2.7 et numpy version 1.6.1
En Python, tous les noms de variables sont des références à des valeurs .
Lorsque Python évalue une affectation, le membre de droite est évalué avant le membre de gauche . arr - 3
crée un nouveau tableau; il ne modifie pas arr
sur place.
arr = arr - 3
fait que la variable locale arr
référence ce nouveau tableau. Il ne modifie pas la valeur initialement référencée par arr
qui a été transmise à foo
. Le nom de variable arr
est simplement lié au nouveau tableau, arr - 3
. De plus, arr
est le nom de la variable locale dans la portée de la fonction foo
. Une fois que la fonction foo
est terminée, il n'y a plus de référence à arr
et Python est libre de récupérer la valeur référencée. Comme le souligne Reti43 , pour que la valeur de arr
affecte a
, foo
doit renvoyer arr
et a
doit être affecté à cette valeur:
def foo(arr):
arr = arr - 3
return arr
# or simply combine both lines into `return arr - 3`
a = foo(a)
En revanche, arr -= 3
, que Python traduit par un appel à la méthode spéciale __iadd__
, modifie le tableau référencé par arr
sur place.
La première fonction calcule (arr - 3)
, puis lui attribue le nom local arr
, ce qui n’affecte pas les données du tableau transmises. Je suppose que dans la deuxième fonction, np.array
remplace l’opérateur -=
et opère à la place des données du tableau.
Python passe le tableau par référence:
$: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 réponse principale fait référence à un phénomène qui se produit même dans le code C compilé, car tous les événements BLAS impliquent une étape de "lecture continue" dans laquelle un nouveau tableau est formé et que l'utilisateur (l'auteur du code dans ce cas) connaît. , ou un nouveau tableau est formé "sous le capot" dans une variable temporaire dont l’utilisateur n’est pas conscient (vous pourriez le voir comme un appel .eval()
).
Cependant, je peux clairement accéder à la mémoire du tableau comme si elle se trouvait dans une portée plus globale que la fonction appelée (c'est-à-dire, setx(...)
); ce qui est exactement ce que "passer par référence" est, en termes d'écriture de code.
Et faisons encore quelques tests pour vérifier la validité de la réponse acceptée:
(continuing the session above)
>>> def minus2(x):
... x[:,:] -= 2
...
>>> minus2(x)
>>> x
array([[-1.,-2.],[-2.,-2.]])
Semble être passé par référence. Faisons un calcul qui va certainement calculer un tableau intermédiaire sous le capot, et voyons si x est modifié comme s'il était passé par référence:
>>> def pow2(x):
... x = x * x
...
>>> pow2(x)
>>> x
array([[-1.,-2.],[-2.,-2.]])
Euh, je pensais que x était passé par référence, mais peut-être que ce n'est pas? - Non, ici, nous avons ombré le x, et python ne propage pas cette "ombrage" vers la portée globale (ce qui violerait le cas d'utilisation de python, qui est un langage de codage de niveau débutant avec beaucoup d'ailes aquatiques et roues d'entraînement).
Cependant, je peux très facilement effectuer cette opération de manière "passe par référence" en forçant la mémoire (qui n'est pas copiée lorsque je soumets x à la fonction) à être modifiée à la place:
>>> def refpow2(x):
... x *= x
...
>>> refpow2(x)
>>> x
array([[1., 4.],[4., 4.]])
Et vous voyez donc que python peut être un peu affiné pour faire ce que vous essayez de faire.