Mir wurde gesagt, dass +=
andere Effekte als die Standardnotation i = i +
haben kann. Gibt es einen Fall, in dem sich i += 1
von i = i + 1
unterscheidet?Wann ist "i + = x" anders als "i = i + x" in Python?
Antwort
Dies hängt vollständig vom Objekt i
ab.
+=
ruft die __iadd__
method (wenn es vorhanden ist - wieder auf __add__
fallen, wenn es nicht vorhanden ist), während +
ruft die __add__
method oder __radd__
method in a few cases .
Aus API Sicht ist __iadd__
soll zum Modifizieren veränderbare Objekte an Ort und Stelle verwendet werden (Rückgabe des Objekts, die mutiert wurde), während __add__
eine neue Instanz etwas zurückgeben sollte. Für unveränderliche Objekte geben beide Methoden eine neue Instanz zurück, aber __iadd__
wird die neue Instanz in den aktuellen Namespace mit demselben Namen wie die alte Instanz einfügen. Deshalb
i = 1
i += 1
scheint zu erhöhen i
. In Wirklichkeit erhalten Sie eine neue Ganzzahl und weisen sie "über" i
- Verlust einer Referenz auf die alte Ganzzahl zu. In diesem Fall ist i += 1
genau dasselbe wie i = i + 1
. Aber bei den meisten veränderbaren Objekten, ist es eine andere Geschichte:
Als ein konkretes Beispiel:
a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a #[1, 2, 3, 1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
im Vergleich zu:
a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
merken, wie im ersten Beispiel, da b
und a
Referenz das gleiche Objekt, wenn ich +=
auf b
benutze, ändert es sich tatsächlich b
(und a
sieht, dass sich auch ändern - schließlich referenziert es die gleiche Liste). Im zweiten Fall jedoch, wenn ich b = b + [1, 2, 3]
mache, nimmt dies die Liste, die b
referenziert und verkettet es mit einer neuen Liste [1, 2, 3]
. Es speichert dann die verkettete Liste im aktuellen Namespace als b
- ohne Rücksicht darauf, was b
war die Zeile zuvor.
Im Ausdruck x + y
, wenn x.__add__
nicht implementiert ist oder wenn x.__add__(y)
kehrt NotImplemented
undx
und y
haben verschiedene Arten, dann x + y
versucht y.__radd__(x)
zu nennen.Also, in dem Fall, in dem Sie haben
foo_instance += bar_instance
wenn Foo
nicht __add__
oder __iadd__
nicht implementiert dann das Ergebnis hier ist die gleiche wie
foo_instance = bar_instance.__radd__(bar_instance, foo_instance)
In dem Ausdruck foo_instance + bar_instance
wird bar_instance.__radd__
versucht, bevor foo_instance.__add__
wenn der Typ bar_instance
eine Unterklasse des Typs foo_instance
ist (z.B. issubclass(Bar, Foo)
). Der Grund dafür ist, dass Bar
in gewissem Sinne ein "höheres" Objekt als Foo
ist, so dass Bar
die Option erhalten sollte, das Verhalten von Foo
zu überschreiben.
Nun, '+ =' ruft '_iaddd' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _. Deshalb ist "i = 1; i + = 1' funktioniert, obwohl es kein 'int .__ iadd__' gibt. Aber anders als diese kleine Nisse, großartige Erklärungen. – abarnert
@abarnert - Ich nahm immer an, dass 'int .__ iadd__' gerade '__add__' genannt wurde. Ich bin froh, heute etwas Neues gelernt zu haben :). – mgilson
@abarnert - Ich nehme an, dass es vielleicht * komplett * ist, 'x + y' nennt' y .__ radd __ (x) 'wenn' x .__ add__' nicht existiert (oder 'NotImplemented' und' x' und ' y 'sind von verschiedenen Typen) – mgilson
unter der Decke, i += 1
tut etwas wie folgt aus:
try:
i = i.__iadd__(1)
except AttributeError:
i = i.__add__(1)
Während i = i + 1
ist so etwas wie dieses:
i = i.__add__(1)
Dies ist eine leichte Simplifizierung, aber Sie bekommen die Idee: Python gibt gibt eine Möglichkeit, +=
speziell zu behandeln, indem eine __iadd__
Methode sowie eine __add__
erstellen.
Die Absicht ist, dass änderbare Typen, wie list
, sich in __iadd__
mutieren wird (und dann self
zurückkehren, wenn Sie etwas sehr heikel tun), während unveränderlichen Typen, wie int
, wird es einfach nicht umsetzen.
Zum Beispiel:
>>> l1 = []
>>> l2 = l1
>>> l1 += [3]
>>> l2
[3]
Da l2
das gleiche Objekt wie l1
ist, und Sie mutiert l1
, Sie mutiert auch l2
.
Aber:
>>> l1 = []
>>> l2 = l1
>>> l1 = l1 + [3]
>>> l2
[]
Hier hast du nicht l1
mutieren; Stattdessen haben Sie eine neue Liste erstellt, l1 + [3]
, und den Namen l1
zurückspringen, um darauf zu zeigen, wobei l2
auf der ursprünglichen Liste angezeigt wird.
(In der +=
Version Sie auch l1
rebinding wurden, ist es nur, dass in diesem Fall, dass Sie es auf die gleichen list
rebinding wurden bereits gebunden war, so kann man in der Regel, dass ein Teil ignorieren.)
heißt '__iadd__' eigentlich' __add__' im Falle eines 'AttributError'? – mgilson
Nun, 'i .__ iadd__' ruft nicht '__add__'; es ist 'i + = 1', das' __add__' aufruft. – abarnert
errr ... Ja, das habe ich gemeint. Interessant. Ich habe nicht bemerkt, dass das automatisch gemacht wurde. – mgilson
Wenn Sie haben nur mit Literalen zu tun, dann i += 1
hat das gleiche Verhalten wie i = i + 1
.
Haben Sie das Gefühl, dass die vorhandenen Antworten diese Tatsache nicht ausreichend verdeutlichen? Dies scheint eine unnötige Antwort angesichts der umfangreichen Antwort von Mgilson. – Guvante
Ja, weil keine der vorhandenen Antworten nur die eigentliche Frage ansprach, gingen sie in alle möglichen Richtungen, während ich die Frage als eine einfachere unter Einbeziehung von Literalen interpretierte. –
In Python hat der Begriff "Literal" zwei verschiedene Bedeutungen, von denen Sie wahrscheinlich beide nicht kennen. Es gibt Literal-Token (die 'None',' -1' oder 'a''b'' nicht enthalten) oder Literal-Strukturen (die List/Tuple/Dict-Anzeigen enthalten). Auf der Ebene der Literalstrukturen hat 'i + = [1]' nicht das gleiche Verhalten wie 'i = i + [1]', obwohl es sich um ein Listenanzeigeliteral handelt. – abarnert
Hier ist ein Beispiel, das direkt i += x
mit i = i + x
vergleicht:
def foo(x):
x = x + [42]
def bar(x):
x += [42]
c = [27]
foo(c); # c is not changed
bar(c); # c is changed to [27, 42]
In einfachen Worten, Sie haben zwei Fälle:
i = i + 1
Dieses eine neue Variable erstellt i mit dem Wert der vorherigen i und erhöht es um eins und speichert es an einem anderen Speicherort.
i += 1
Dies erzeugt keine neue Variable, sondern erhöht die Variable i an derselben Speicherstelle. Dies ist viel effizienter als das vorherige.
- 1. in C/C++ ist x [i] * Y [i ++] immer gleich x [i] * y [i]
- 2. python3 Fehler __getitem__ = Lambda-x, i: x._get_current_object() [i]
- 3. Warum ist i = i + 1 schneller als i ++?
- 4. Warum x [i] [:] = x [:] [i] wo x ist eine Liste von Listen?
- 5. Herstellung printf printf x [i] wo ist der actuall Wert von i und einfach nicht „x [i]“
- 6. Warum ist B = numpy.dot (A, x) so viel langsamer durch B [i,:,:] = numpy.dot (A [i,:,:], x))?
- 7. Unterschied zwischen ++ i und i ++
- 8. i = i ++; ist nicht definiert. Ist i = foo (i ++) auch undefiniert?
- 9. Was bedeutet {} in "xargs -I {} unrar x {}"?
- 10. Warum ist Math.sqrt (i * i) .floor == ich?
- 11. Ist etwas wie "für (i = 1; i <= 10; printf ("% d \ n "; i), i ++) gültig und UB-frei in C?
- 12. Ist "a +++ i" gleich "(a ++) + i" in C
- 13. Ist (* i) .member weniger effizient als i-> Mitglied
- 14. ++ i-Operator in Python
- 15. Unterschied zwischen (++ i) und (i ++)
- 16. permute i und T [i]
- 17. Warum Ausführungszeit von "für ($ i = 1; $ i -le 1000000; $ i ++) {}" schneller als "für ([int] $ i = 1; $ i -le 1000000; $ i ++) {}" in Powershell
- 18. Vergleichbarer Code zu Array [++ i] und Array [i ++] in Python?
- 19. Analyse der Differenz zwischen i + = 1 und i = i + 1
- 20. wie i
- 21. Erläutern Alternative PHP for Loop-Syntax: für ($ i = 1, $ j = 0; $ i <= 10; $ j + = $ i, drucken $ i, $ i ++);
- 22. Return-Liste [i] = i Index einer Liste
- 23. Warum l.insert (0, i) ist langsamer als l.append (i) in Python?
- 24. Unterschied zwischen i ++ und ++ i in einer for-Schleife
- 25. Alternative zu 'für i in xrange (len (x))'
- 26. was ist das Mittel von i & = (i-1) in Java
- 27. Rekursive Funktion, die das Subarray X [i, ..., j] umkehrt. Elemente von Index i bis j einschließlich
- 28. Warum? A [i] = b; i ++ `Leistung besser als` a [i ++] = b '?
- 29. Ist (++ i) ++ undefiniertes Verhalten?
- 30. Warum ist i ++++++++ ich in Python gültig?
'+ =' verhält sich wie 'extend()' im Falle von Listen. –
@AshwiniChaudhary Das ist eine ziemlich feine Unterscheidung, wenn man bedenkt, dass "i = [1,2,3]; i = i + [4,5,6]; i == [1,2,3,4,5,6]" ist "wahr". Viele Entwickler bemerken möglicherweise nicht, dass sich 'id (i) 'für eine Operation ändert, aber nicht für die andere. – kojiro
@kojiro - Obwohl es eine subtile Unterscheidung ist, denke ich, dass es eine wichtige ist. – mgilson