Perché l'assegnazione a un elenco vuoto (ad es. [] = “”) Non è un errore?


110

In Python 3.4, sto digitando

[] = "" 

e funziona bene, non viene sollevata alcuna eccezione. Anche se ovviamente []non è uguale a ""dopo.

[] = ()

funziona anche bene.

"" = []

solleva un'eccezione come previsto però,

() = ""

solleva un'eccezione come previsto però. Allora, cosa sta succedendo?

Risposte:


132

Non stai confrontando per l'uguaglianza. Stai assegnando .

Python ti consente di assegnare a più obiettivi:

foo, bar = 1, 2

assegna i due valori a fooe bar, rispettivamente. Tutto ciò di cui hai bisogno è una sequenza o iterabile sul lato destro e un elenco o tupla di nomi sulla sinistra.

Quando lo fai:

[] = ""

hai assegnato una sequenza vuota (le stringhe vuote sono ancora sequenze) a un elenco vuoto di nomi.

È essenzialmente la stessa cosa che fare:

[foo, bar, baz] = "abc"

dove finisci con foo = "a", bar = "b"e baz = "c", ma con meno personaggi.

Tuttavia, non è possibile assegnare a una stringa, quindi ""sul lato sinistro di un'assegnazione non funziona mai ed è sempre un errore di sintassi.

Consulta la documentazione delle dichiarazioni di assegnazione :

Una dichiarazione di assegnazione valuta la lista delle espressioni (ricorda che questa può essere una singola espressione o una lista separata da virgole, quest'ultima produce una tupla) e assegna il singolo oggetto risultante a ciascuna delle liste di destinazione, da sinistra a destra.

e

L'assegnazione di un oggetto a un elenco di destinazione, facoltativamente racchiuso tra parentesi o parentesi quadre , viene definita ricorsivamente come segue.

Enfasi mia .

Che Python non generi un errore di sintassi per l'elenco vuoto è in realtà un po 'un bug! La grammatica ufficialmente documentata non consente un elenco di destinazione vuoto e per il vuoto ()si ottiene un errore. Vedi bug 23275 ; è considerato un bug innocuo:

Il punto di partenza è riconoscere che questo sistema esiste da molto tempo ed è innocuo.

Vedi anche Perché è valido assegnare a un elenco vuoto ma non a una tupla vuota?


36

Segue le regole della sezione Dichiarazioni di assegnazione dalla documentazione,

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

Se target listè un elenco di target separati da virgole: L'oggetto deve essere un iterabile con lo stesso numero di elementi quanti sono i target nell'elenco target e gli elementi vengono assegnati, da sinistra a destra, ai target corrispondenti.

L'oggetto deve essere una sequenza con lo stesso numero di elementi quanti sono i target nell'elenco dei target e gli elementi vengono assegnati, da sinistra a destra, ai target corrispondenti.

Quindi, quando dici

[] = ""

"" è un iterabile (qualsiasi stringa python valida è un iterabile) e viene decompresso sugli elementi della lista.

Per esempio,

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

Dato che hai una stringa vuota e un elenco vuoto, non c'è niente da decomprimere. Quindi, nessun errore.

Ma prova questo

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

Nel [] = "1"caso, stai cercando di decomprimere la stringa"1" su un elenco vuoto di variabili. Quindi si lamenta di "troppi valori da decomprimere (previsto 0)".

Allo stesso modo, in [a] = "" caso cui tu abbia una stringa vuota, quindi niente da decomprimere davvero, ma lo stai decomprimendo su una variabile, il che, di nuovo, non è possibile. Questo è il motivo per cui si lamenta "sono necessari più di 0 valori per decomprimere".

A parte questo, come hai notato,

>>> [] = ()

inoltre non genera errori, perché ()è una tupla vuota.

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

e quando viene decompresso su un elenco vuoto, non c'è nulla da decomprimere. Quindi nessun errore.


Ma quando lo fai

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

come dice il messaggio di errore, stai tentando di assegnare una stringa letterale. Che non è possibile. Questo è il motivo per cui ricevi gli errori. È come dire

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

Interni

Internamente, questa operazione di assegnazione verrà tradotta in UNPACK_SEQUENCEcodice operativo ,

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

Qui, poiché la stringa è vuota, UNPACK_SEQUENCEdecomprime i 0tempi. Ma quando hai qualcosa del genere

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

la sequenza 123viene decompressa nello stack, da destra a sinistra. Quindi, la parte superiore della pila sarebbe 1e la prossima sarebbe 2e l'ultima sarebbe 3. Quindi assegna dalla cima dello stack alle variabili dal lato sinistro un'espressione una per una.


A proposito, in Python, è così che puoi eseguire più assegnazioni nella stessa espressione. Per esempio,

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

questo funziona perché, i valori della mano destra vengono utilizzati per costruire una tupla e quindi verrà decompressa sui valori del lato sinistro.

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

ma la classica tecnica di scambio a, b = b, autilizza la rotazione degli elementi nella parte superiore della pila. Se hai solo due o tre elementi, vengono trattati con istruzioni speciali ROT_TWOe ROT_THREEinvece di costruire la tupla e spacchettarli.

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

Puoi anche usare dis('[] = ""')senza chiamare compile().
Andrea Corbellini

Puoi descrivere cosa succede se stai scambiando più di tre variabili / elementi, usando il metodo nel tuo ultimo esempio?
nanofarad

@hexafraction Costruirà una nuova tupla con tutti gli elementi sul lato destro e poi li decomprimerà sulle variabili sul lato sinistro.
quattro

@hexafraction: vedi Come funziona
Martijn Pieters
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.