Remarque: l'utilisation d'opérations sur place sur des tableaux NumPy qui partagent de la mémoire n'est plus un problème dans la version 1.13.0 et les versions ultérieures (voir les détails ici ). Les deux opérations produiront le même résultat. Cette réponse s'applique uniquement aux versions antérieures de NumPy.
La mutation des tableaux lors de leur utilisation dans les calculs peut entraîner des résultats inattendus!
Dans l'exemple de la question, la soustraction avec -=
modifie le deuxième élément de a
, puis utilise immédiatement ce deuxième élément modifié dans l'opération sur le troisième élément de a
.
Voici ce qui se passe a[1:] -= a[:-1]
étape par étape:
a
est le tableau avec les données [1, 2, 3]
.
Nous avons deux vues sur ces données: a[1:]
est [2, 3]
, et a[:-1]
est [1, 2]
.
La soustraction sur place -=
commence. Le premier élément de a[:-1]
, 1, est soustrait du premier élément de a[1:]
. Cela a changé a
pour être [1, 1, 3]
. Maintenant, nous avons a[1:]
une vue des données [1, 3]
et a[:-1]
une vue des données [1, 1]
(le deuxième élément du tableau a
a été modifié).
a[:-1]
est maintenant [1, 1]
et NumPy doit maintenant soustraire son deuxième élément qui est 1 (et non plus 2!) du deuxième élément de a[1:]
. Cela fait a[1:]
une vue des valeurs [1, 2]
.
a
est maintenant un tableau avec les valeurs [1, 1, 2]
.
b[1:] = b[1:] - b[:-1]
n'a pas ce problème car b[1:] - b[:-1]
crée d'abord un nouveau tableau, puis affecte les valeurs de ce tableau à b[1:]
. Il ne se modifie pas b
lors de la soustraction, donc les vues b[1:]
et b[:-1]
ne changent pas.
Le conseil général est d'éviter de modifier une vue en place avec une autre si elles se chevauchent. Cela inclut les opérateurs -=
, *=
etc. et l'utilisation du out
paramètre dans les fonctions universelles (comme np.subtract
et np.multiply
) pour réécrire dans l'un des tableaux.
En interne, la différence est que ceci:
équivaut à ceci:
alors que ce:
correspond à ceci:
Dans certains cas,
__sub__()
et__isub__()
fonctionnent de la même manière. Mais les objets mutables doivent muter et se retourner lors de l'utilisation__isub__()
, alors qu'ils doivent renvoyer un nouvel objet avec__sub__()
.L'application d'opérations de tranche sur des objets numpy crée des vues sur eux, donc leur utilisation permet d'accéder directement à la mémoire de l'objet "d'origine".
la source
Les documents disent:
En règle générale, la soustraction augmentée (
x-=y
) estx.__isub__(y)
, pour l' opération IN -place SI possible, lorsque la soustraction normale (x = x-y
) l'estx=x.__sub__(y)
. Sur les objets non mutables comme les entiers, c'est équivalent. Mais pour les mutables comme les tableaux ou les listes, comme dans votre exemple, il peut s'agir de choses très différentes.la source