2015-08-22 9 views
22

(Keine Sorge, das über dem Auspacken Tupeln nicht eine andere Frage.)Python Mehrfachzuordnung Statements Ein Line-

In Python, eine Aussage wie foo = bar = baz = 5 ordnet die Variablen foo, bar und baz bis 5. er ordnet diese Variablen von links nach rechts, wie durch fiese Beispiele wie

>>> foo[0] = foo = [0] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'foo' is not defined 
>>> foo = foo[0] = [0] 
>>> foo 
[[...]] 
>>> foo[0] 
[[...]] 
>>> foo is foo[0] 
True 

Aber die python language reference Staaten, die Zuweisungsanweisungen haben die Form

(target_list "=")+ (expression_list | yield_expression) 
nachgewiesen werden

und bei Zuweisung wird zuerst die expression_list ausgewertet und dann erfolgt die Zuweisung. Wie kann die Zeile foo = bar = 5 gültig sein, wenn bar = 5 keine expression_list ist? Wie werden diese Mehrfachzuweisungen in einer Zeile analysiert und ausgewertet? Lies ich die Sprachreferenz falsch?

+5

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'. –

+0

Aha! Das habe ich vermisst. Wenn Sie dies zur Antwort machen, kann ich es akzeptieren. Vielen Dank! – mwcvitkovic

Antwort

11

Alle Kredit geht an @MarkDickinson, die dies in einem Kommentar geantwortet:

Beachten Sie die + in (target_list "=")+, die eine oder mehrere Kopien bedeutet. In foo = bar = 5 gibt es zwei (target_list "=") Produktionen und der expression_list Teil ist nur 5

Alle target_list Produktionen (dh Dinge, die wie foo = aussehen) in einer Zuweisungsanweisung zugewiesen bekommen, von links nach rechts, auf die expression_list auf die rechtes Ende der Aussage, nachdem die expression_list ausgewertet wurde.

Und natürlich die übliche ‚Tupel-Auspacken‘ Zuweisung Syntax innerhalb dieser Syntax funktioniert, Sie Dinge wie

>>> foo, boo, moo = boo[0], moo[0], foo[0] = moo[0], foo[0], boo[0] = [0], [0], [0] 
>>> foo 
[[[[...]]]] 
>>> foo[0] is boo 
True 
>>> foo[0][0] is moo 
True 
>>> foo[0][0][0] is foo 
True 
6

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.

+0

Ich dachte, das gleiche über die rechte Assoziativität zunächst auch, aber ich glaube nicht, dass das, was Python tut. Wenn es so wäre, wäre 'foo [0] = foo = [0]' eine gültige Python-Anweisung, ist es aber nicht. Vielmehr ist 'foo = foo [0] = [0]' eine gültige Aussage - eine, die äquivalent zu 'foo = [0] ist; foo [0] = foo'. Um Ihr Beispiel zu verwenden, wird "x = y = z = 5" als seltsam-links-assoziativ als "x = 5" bewertet; y = 5; z = 5'. Das Grundstück verdickt sich ... – mwcvitkovic

+0

Interessante Ideen! @ MarkDickinson wies jedoch darauf hin, dass ich die obige Sprachangabe falsch gelesen habe. Nun ist die ganze Sache macht Sinn, zusammen mit der Tatsache, dass Sie Dinge wie 'foo tun können, boo = foo [0], boo [0] = [0], [0]' – mwcvitkovic

+0

@cvitkovm ich herausgefunden, was los war mit die 'foo' Fälle. Der Zirkelverweis wird eingerichtet, weil Zuordnungen, bei denen Listen beteiligt sind, Verweise und nicht die Listen selbst kopieren. Danke, dass Sie eine so interessante Frage gestellt haben. –

3

bar = 5 ist kein Ausdruck. Die Mehrfachzuweisung ist eine separate Anweisung von der Zuweisungsanweisung; der Ausdruck ist alles rechts von der rechten =.

Eine gute Möglichkeit, darüber nachzudenken ist, dass die am weitesten rechts stehenden = der Haupt Separator ist; alles rechts davon passiert von links nach rechts, und alles links davon passiert auch von links nach rechts.

+0

Nun, ich stimme definitiv zu, dass der Code evaluiert wird, aber ich weiß schon, was der Code macht.Was ich herausfinden möchte, ist, wie Python Anweisungen wie 'foo = bar = 5 'parst, wenn es so aussieht, als würde die Syntax mit dem kollidieren, was in der Sprachreferenz angegeben ist. Könnten Sie einen Hinweis geben, wo Sie festgestellt haben, dass "die Mehrfachzuweisung eine separate Aussage aus der Zuweisungsanweisung ist ..."? Ich konnte nur 'Assignment Statements' in der Sprachreferenz finden. – mwcvitkovic

+0

Es könnte funktionieren, um zu überprüfen, was genau die cpython-Implementierung tut. –

+0

@cvitkovm Ich weiß, weil ich zum [astor] (https://github.com/berkerpeksag/astor/) -Projekt beigetragen habe, dass eine Zuweisung als einzelner AST-Knoten gespeichert wird, aber möglicherweise mit [mehreren Zielen auf dem links] (https://github.com/berkerpeksag/astor/blob/master/astor/code_gen.py#L238). Wenn Sie sich diesen Link ansehen, sehen Sie, dass für das Rekonstruieren der Quelle das Drucken eines Gleichheitszeichens für jedes Ziel erforderlich ist. –