Warum ist das Zuweisen zu einer leeren Liste (z. B. [] = “”) kein Fehler?

110

In Python 3.4 tippe ich

[] = "" 

und es funktioniert gut, es wird keine Ausnahme ausgelöst. Obwohl natürlich []nicht gleich ""danach ist.

[] = ()

funktioniert auch gut.

"" = []

löst erwartungsgemäß eine Ausnahme aus,

() = ""

löst jedoch erwartungsgemäß eine Ausnahme aus. So was ist los?

Vardd
quelle

Antworten:

132

Sie vergleichen nicht für Gleichheit. Sie zuweisen .

Mit Python können Sie mehreren Zielen zuweisen:

foo, bar = 1, 2

Weist die beiden Werte foound bar, respectively. Alles, was Sie brauchen, ist eine Sequenz oder Iterable auf der rechten Seite und eine Liste oder ein Tupel von Namen auf der linken Seite.

Wenn Sie das tun:

[] = ""

Sie haben einer leeren Namensliste eine leere Sequenz zugewiesen (leere Zeichenfolgen sind noch Sequenzen).

Es ist im Wesentlichen dasselbe wie zu tun:

[foo, bar, baz] = "abc"

wo Sie am Ende mit foo = "a", bar = "b"und baz = "c", aber mit weniger Zeichen.

Sie können jedoch keinen String zuweisen, sodass ""die linke Seite einer Zuweisung niemals funktioniert und immer ein Syntaxfehler ist.

Siehe die Dokumentation zu den Zuweisungsanweisungen :

Eine Zuweisungsanweisung wertet die Ausdrucksliste aus (denken Sie daran, dass dies ein einzelner Ausdruck oder eine durch Kommas getrennte Liste sein kann, wobei letztere ein Tupel ergibt) und weist das einzelne resultierende Objekt jeder der Ziellisten von links nach rechts zu.

und

Die Zuordnung eines Objekts zu einer Zielliste, die optional in Klammern oder eckigen Klammern eingeschlossen ist , wird rekursiv wie folgt definiert.

Hervorhebung von mir .

Dass Python keinen Syntaxfehler für die leere Liste auslöst, ist eigentlich ein kleiner Fehler! Die offiziell dokumentierte Grammatik erlaubt keine leere Zielliste, und für die leere erhalten ()Sie eine Fehlermeldung. Siehe Fehler 23275 ; Es wird als harmloser Fehler angesehen:

Der Ausgangspunkt ist zu erkennen, dass dies schon sehr lange existiert und harmlos ist.

Siehe auch Warum ist es gültig, eine leere Liste, aber kein leeres Tupel zuzuweisen?

Martijn Pieters
quelle
36

Es folgt den Regeln des Abschnitts " Zuweisungsanweisungen" aus der Dokumentation.

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

Wenn target listes sich um eine durch Kommas getrennte Liste von Zielen handelt: Das Objekt muss iterierbar mit der gleichen Anzahl von Elementen sein, wie Ziele in der Zielliste vorhanden sind, und die Elemente werden von links nach rechts den entsprechenden Zielen zugewiesen.

Das Objekt muss eine Sequenz mit der gleichen Anzahl von Elementen sein, wie es Ziele in der Zielliste gibt, und die Elemente müssen von links nach rechts den entsprechenden Zielen zugewiesen werden.

Also, wenn du sagst

[] = ""

"" ist eine iterable (jede gültige Python-Zeichenfolge ist eine iterable) und wird über die Elemente der Liste entpackt.

Beispielsweise,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

Da Sie eine leere Zeichenfolge und eine leere Liste haben, müssen Sie nichts entpacken. Also kein Fehler.

Aber versuchen Sie es

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

In diesem [] = "1"Fall versuchen Sie, die Zeichenfolge "1"über eine leere Liste von Variablen zu entpacken . Es beschwert sich also mit "zu vielen Werten zum Entpacken (erwartete 0)".

Genauso wie für den [a] = ""Fall, dass Sie eine leere Zeichenfolge haben, also nichts wirklich zu entpacken, aber Sie entpacken sie über eine Variable, was wiederum nicht möglich ist. Aus diesem Grund wird beanstandet, dass zum Entpacken mehr als 0 Werte erforderlich sind.

Abgesehen davon, wie Sie bemerkt haben,

>>> [] = ()

wirft auch keinen Fehler, da ()es sich um ein leeres Tupel handelt.

>>> ()
()
>>> type(())
<class 'tuple'>

und wenn es über eine leere Liste entpackt wird, gibt es nichts zu entpacken. Also kein Fehler.


Aber wenn du es tust

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

Wie in der Fehlermeldung angegeben, versuchen Sie, einem Zeichenfolgenliteral zuzuweisen. Welches ist nicht möglich. Deshalb erhalten Sie die Fehler. Es ist wie zu sagen

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

Interna

Intern wird diese Zuweisungsoperation in UNPACK_SEQUENCEOp-Code übersetzt.

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

Hier wird, da die Zeichenfolge leer ist, mal UNPACK_SEQUENCEentpackt 0. Aber wenn Sie so etwas haben

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

Die Sequenz 123wird von rechts nach links in den Stapel entpackt. Die Spitze des Stapels wäre also 1und die nächste wäre 2und die letzte wäre 3. Anschließend werden die Variablen aus dem Ausdruck auf der linken Seite nacheinander von oben im Stapel zugewiesen.


Übrigens können Sie in Python auf diese Weise mehrere Zuweisungen in demselben Ausdruck vornehmen. Beispielsweise,

a, b, c, d, e, f = u, v, w, x, y, z

Dies funktioniert, weil die Werte auf der rechten Seite zum Erstellen eines Tupels verwendet werden und es dann über die Werte auf der linken Seite entpackt wird.

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

Bei der klassischen Austauschtechnik werden jedoch a, b = b, adie Elemente oben im Stapel gedreht. Wenn Sie nur zwei oder drei Elemente haben dann werden sie mit speziellen behandelt ROT_TWOund ROT_THREEdafür , das Tupel des Konstruierens und Auspacken.

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
thefourtheye
quelle
Sie können auch dis('[] = ""')ohne Anruf verwenden compile().
Andrea Corbellini
Können Sie beschreiben, was passiert, wenn Sie mehr als drei Variablen / Elemente mit der Methode in Ihrem letzten Beispiel austauschen?
Nanofarad
@hexafraction Es wird ein neues Tupel mit allen Elementen auf der rechten Seite erstellt und dann über die Variablen auf der linken Seite entpackt.
thefourtheye
@hexafraction: siehe Wie funktioniert das
Martijn Pieters