Mark Dickinson erklärte die Syntax von dem, was passiert, aber die seltsamen Beispiele mit foo
zeigen, dass die Semantik kontraintuitiv sein kann.
In C, =
eine rechts assoziativen Operator ist, der so die RHS der Zuweisung als einen Wert zurückgibt, wenn Sie x = y = 5
schreiben, wird y=5
zuerst ausgewertet (5 bis y
im Prozess zuordnen), und dieser Wert (5) dann zugewiesen zu x
.
Bevor ich diese Frage gelesen habe, nahm ich naiv an, dass in Python ungefähr das Gleiche passiert. Aber in Python =
ist nicht ein Ausdruck (zum Beispiel 2 + (x = 5)
ist ein Syntaxfehler). Also muss Python mehrere Zuweisungen auf andere Weise erreichen.
Wir zerlegen, anstatt Vermutung:
>>> import dis
>>> dis.dis('x = y = 5')
1 0 LOAD_CONST 0 (5)
3 DUP_TOP
4 STORE_NAME 0 (x)
7 STORE_NAME 1 (y)
10 LOAD_CONST 1 (None)
13 RETURN_VALUE
Siehe this für eine Beschreibung der Bytecode-Instruktionen.
Die erste Anweisung drückt 5 auf den Stapel.
Der zweite Befehl dupliziert es - so nun die Oberseite des Stapels hat zwei 5S
STORE_NAME(name)
„Implementiert name = TOS“ entsprechend der Bytecode Dokumentation
So STORE_Name(x)
implementiert x = 5
(der 5 auf oben auf dem Stapel), knallt diese 5 aus dem Stapel, wie es geht, nach dem implementiert y = 5
mit den anderen 5 auf dem Stapel.
Der Rest des Bytecodes ist hier nicht direkt relevant.
Im Fall von foo = foo[0] = [0]
ist der Byte-Code wegen der Listen komplizierter, hat aber eine grundlegend ähnliche Struktur. Die Schlüsselbeobachtung ist, dass, sobald die Liste [0]
erstellt und auf dem Stapel abgelegt wurde, die Anweisung DUP_TOP
keine weitere Kopie von [0]
auf den Stapel legt, stattdessen wird eine weitere Referenz auf die Liste gesetzt.Mit anderen Worten, in diesem Stadium sind die obersten zwei Elemente des Stapels Aliase für die gleiche Liste. Dies kann in dem etwas einfacheren Fall gesehen am deutlichsten:
>>> x = y = [0]
>>> x[0] = 5
>>> y[0]
5
Wenn foo = foo[0] = [0]
ausgeführt wird, wird die Liste [0]
wird zunächst auf foo
zugewiesen und dann ein Alias von derselben Liste wird foo[0]
zugeordnet. Aus diesem Grund ist foo
ein Zirkelverweis.
Beachten Sie die '+' in '(target_list" = ") +', was eine oder mehrere Kopien bedeutet. In 'foo = bar = 5' gibt es zwei' (target_list "=") 'Produktionen und der' expression_list' Teil ist nur '5'. –
Aha! Das habe ich vermisst. Wenn Sie dies zur Antwort machen, kann ich es akzeptieren. Vielen Dank! – mwcvitkovic