web-dev-qa-db-fra.com

Les tableaux numpy sont-ils passés par référence?

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

44
nisah

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.

59
unutbu

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.

8
fferen

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. 

0
bordeo