Dlaczego przypisanie do pustej listy (np. [] = „”) Nie jest błędem?

110

W Pythonie 3.4 piszę

[] = "" 

i działa dobrze, nie jest zgłaszany żaden wyjątek. Chociaż oczywiście []nie równa się ""później.

[] = ()

działa również dobrze.

"" = []

zgłasza jednak wyjątek zgodnie z oczekiwaniami,

() = ""

zgłasza jednak wyjątek, zgodnie z oczekiwaniami. Więc co się dzieje?

Vardd
źródło

Odpowiedzi:

132

Nie porównujesz się dla równości. Jesteś przypisywania .

Python umożliwia przypisanie do wielu celów:

foo, bar = 1, 2

przypisuje dwie wartości odpowiednio do fooi bar. Wszystko, czego potrzebujesz, to sekwencja lub iterowalna po prawej stronie i lista lub krotka nazwisk po lewej stronie.

Kiedy robisz:

[] = ""

przypisałeś pustą sekwencję (puste łańcuchy to nadal sekwencje) do pustej listy nazw.

Zasadniczo jest to to samo, co robienie:

[foo, bar, baz] = "abc"

gdzie kończy się z foo = "a", bar = "b"a baz = "c", ale z mniejszą liczbą znaków.

Nie możesz jednak przypisać do łańcucha, więc ""po lewej stronie przypisania nigdy nie działa i zawsze jest to błąd składniowy.

Zobacz dokumentację Deklaracje przypisania :

Instrukcja przypisania ocenia listę wyrażeń (pamiętaj, że może to być pojedyncze wyrażenie lub lista oddzielona przecinkami, przy czym ta ostatnia daje krotkę) i przypisuje pojedynczy wynikowy obiekt do każdej z list docelowych, od lewej do prawej.

i

Przypisanie obiektu do listy docelowej, opcjonalnie ujętej w nawiasy kwadratowe lub nawiasy kwadratowe , jest rekurencyjnie definiowane w następujący sposób.

Podkreśl moje .

To, że Python nie zgłasza błędu składniowego dla pustej listy, jest w rzeczywistości trochę błędem! Oficjalnie udokumentowana gramatyka nie zezwala na pustą listę celów, a dla pustej pojawia ()się błąd. Zobacz błąd 23275 ; jest uważany za nieszkodliwy błąd:

Punktem wyjścia jest uznanie, że istnieje to od bardzo dawna i jest nieszkodliwe.

Zobacz także Dlaczego prawidłowe jest przypisywanie do pustej listy, ale nie do pustej krotki?

Martijn Pieters
źródło
36

Jest zgodny z zasadami sekcji Instrukcje przypisania z dokumentacji,

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

Jeśli target listjest to lista celów rozdzielonych przecinkami: obiekt musi być iterowalny i zawierać taką samą liczbę elementów, jak cele na liście celów, a elementy są przypisane, od lewej do prawej, do odpowiednich celów.

Obiekt musi być sekwencją z taką samą liczbą elementów, jak liczba celów na liście celów, a elementy są przypisane, od lewej do prawej, do odpowiednich celów.

Więc kiedy mówisz

[] = ""

"" jest iterowalny (każdy poprawny łańcuch w Pythonie jest iterowalny) i jest rozpakowywany po elementach listy.

Na przykład,

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

Ponieważ masz pusty ciąg i pustą listę, nie ma nic do rozpakowania. Więc nie ma błędu.

Ale spróbuj tego

>>> [] = "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

W takim [] = "1"przypadku próbujesz rozpakować łańcuch "1"na pustej liście zmiennych. Więc narzeka z "zbyt wieloma wartościami do rozpakowania (oczekiwane 0)".

Tak samo, w [a] = ""przypadku, gdy masz pusty ciąg, więc nie ma nic do rozpakowania, ale rozpakowujesz go na jedną zmienną, co znowu jest niemożliwe. Dlatego narzeka, że ​​„do rozpakowania potrzeba więcej niż 0 wartości”.

Poza tym, jak zauważyłeś,

>>> [] = ()

również nie zgłasza błędu, ponieważ ()jest pustą krotką.

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

a kiedy jest rozpakowywany na pustej liście, nie ma nic do rozpakowania. Więc nie ma błędu.


Ale kiedy to zrobisz

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

jak mówi komunikat o błędzie, próbujesz przypisać do literału łańcuchowego. Co nie jest możliwe. Dlatego otrzymujesz błędy. To jak mówienie

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

Wewnętrzne

Wewnętrznie ta operacja przypisania zostanie przetłumaczona UNPACK_SEQUENCEna kod operacyjny ,

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

Tutaj, ponieważ ciąg jest pusty, UNPACK_SEQUENCErozpakowuje 0razy. Ale kiedy masz coś takiego

>>> 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

sekwencja 123jest wypakowywana na stos, od prawej do lewej. Tak więc, szczyt stosu byłby, 1następny byłby 2i ostatni byłby 3. Następnie przypisuje z góry stosu zmienne z wyrażenia po lewej stronie, jeden po drugim.


Przy okazji, w Pythonie w ten sposób można wykonać wiele przypisań w tym samym wyrażeniu. Na przykład,

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

to działa, ponieważ wartości po prawej stronie są używane do konstruowania krotki, a następnie zostaną rozpakowane na wartościach po lewej stronie.

>>> 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

ale klasyczna technika zamiany a, b = b, awykorzystuje rotację elementów na szczycie stosu. Jeśli masz tylko dwa lub trzy elementy, zamiast konstruowania krotki i rozpakowywania są traktowane specjalnymi instrukcjami ROT_TWOi ROT_THREEinstrukcjami.

>>> 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
na czworo
źródło
Możesz także używać dis('[] = ""')bez dzwonienia compile().
Andrea Corbellini
Czy możesz opisać, co się stanie, jeśli zamieniasz więcej niż trzy zmienne / elementy, używając metody z twojego ostatniego przykładu?
nanofarad
@hexafraction Zbuduje nową krotkę ze wszystkimi elementami po prawej stronie, a następnie rozpakuje je nad zmiennymi po lewej stronie.
thefourtheye
@hexafraction: zobacz Jak przebiega zamiana członków w
Martijn Pieters