web-dev-qa-db-de.com

Unterschied zwischen a - = b und a = a - b in Python

Ich habe vor kurzem diese Lösung für die Mittelung aller N Zeilen der Matrix angewendet. Obwohl die Lösung im Allgemeinen funktioniert, hatte ich Probleme bei der Anwendung auf ein 7x1-Array. Ich habe festgestellt, dass das Problem bei der Verwendung des -= Operator. Um ein kleines Beispiel zu machen:

import numpy as np

a = np.array([1,2,3])
b = np.copy(a)

a[1:] -= a[:-1]
b[1:] = b[1:] - b[:-1]

print a
print b

welche Ausgänge:

[1 1 2]
[1 1 1]

Also, im Fall eines Arrays a -= b erzeugt ein anderes Ergebnis als a = a - b. Ich dachte bis jetzt, dass diese beiden Wege genau gleich sind. Was ist der Unterschied?

Wie kommt es, dass die von mir erwähnte Methode zum Summieren von jeweils N Zeilen in einer Matrix z. für eine 7x4 Matrix aber nicht für ein 7x1 Array?

88
iasonas

Hinweis: Die Verwendung von In-Place-Vorgängen auf NumPy-Arrays, die Speicher gemeinsam nutzen, ist ab Version 1.13.0 kein Problem mehr (siehe Details hier ). Die beiden Operationen führen zu demselben Ergebnis. Diese Antwort gilt nur für frühere Versionen von NumPy.


Das Mutieren von Arrays, während sie in Berechnungen verwendet werden, kann zu unerwarteten Ergebnissen führen!

In dem Beispiel in der Frage ändert die Subtraktion mit -= Das zweite Element von a und verwendet dann sofort das geänderte zweites Element in der Operation für das dritte Element von a.

Folgendes passiert mit a[1:] -= a[:-1] Schritt für Schritt:

  • a ist das Array mit den Daten [1, 2, 3].

  • Wir haben zwei Ansichten zu diesen Daten: a[1:] Ist [2, 3] Und a[:-1] Ist [1, 2].

  • Die In-Place-Subtraktion -= Beginnt. Das erste Element von a[:-1], 1, wird vom ersten Element von a[1:] Abgezogen. Dies hat a zu [1, 1, 3] Geändert. Jetzt haben wir, dass a[1:] Eine Ansicht der Daten [1, 3] Ist und a[:-1] Eine Ansicht der Daten [1, 1] Ist (das zweite Element des Arrays a wurde geändert).

  • a[:-1] Ist jetzt [1, 1] Und NumPy muss jetzt sein zweites Element abziehen, das 1 (nicht mehr 2!) Ist das zweite Element von a[1:]. Dadurch wird a[1:] Eine Ansicht der Werte [1, 2].

  • a ist jetzt ein Array mit den Werten [1, 1, 2].

b[1:] = b[1:] - b[:-1] Hat dieses Problem nicht, da b[1:] - b[:-1] Zuerst ein neues Array erstellt und dann die Werte in diesem Array zuweist b[1:]. Es ändert b selbst während der Subtraktion nicht, daher ändern sich die Ansichten b[1:] Und b[:-1] Nicht.


Es wird allgemein empfohlen, eine Ansicht nicht durch eine andere zu ersetzen, wenn sie sich überschneidet. Dies schließt die Operatoren -=, *= Usw. und die Verwendung des Parameters out in universellen Funktionen (wie np.subtract Und np.multiply) Mit ein Schreiben Sie zurück zu einem der Arrays.

79
Alex Riley

Intern besteht der Unterschied darin, dass:

a[1:] -= a[:-1]

ist gleichbedeutend mit:

a[1:] = a[1:].__isub__(a[:-1])
a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None)))

während dieser:

b[1:] = b[1:] - b[:-1]

karten dazu:

b[1:] = b[1:].__sub__(b[:-1])
b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None)))

In einigen Fällen funktionieren __sub__() und __isub__() auf ähnliche Weise. Aber veränderbare Objekte sollten mutieren und sich selbst zurückgeben, wenn sie __isub__() verwenden, während sie mit __sub__() ein neues Objekt zurückgeben sollten.

Durch das Anwenden von Slice-Operationen auf numpy-Objekte werden Ansichten auf ihnen erstellt, sodass durch ihre Verwendung direkt auf den Speicher des "ursprünglichen" Objekts zugegriffen werden kann.

43
glglgl

Die Dokumente sagen:

Die Idee hinter der erweiterten Zuweisung in Python) ist, dass es nicht nur einfacher ist, die übliche Vorgehensweise zum Speichern des Ergebnisses einer Binäroperation in ihrem linken Operanden zu schreiben, sondern auch auf eine Weise Damit der betreffende linke Operand weiß, dass er "auf sich selbst" operieren soll, anstatt eine modifizierte Kopie von sich selbst zu erstellen.

Als Daumenregel lautet die erweiterte Subtraktion (x-=y) x.__isub__(y) für [~ # ~] in [~ # ~] - Operation platzieren [~ # ~] wenn [~ # ~] möglich ist, wenn normale Subtraktion (x = x-y) ist x=x.__sub__(y). Bei nicht veränderlichen Objekten wie ganzen Zahlen ist dies äquivalent. Aber für veränderbare wie Arrays oder Listen, wie in Ihrem Beispiel, können sie sehr unterschiedliche Dinge sein.

11
B. M.