Disimballaggio, spacchettamento esteso e spacchettamento esteso annidato


105

Considera le seguenti espressioni. Notare che alcune espressioni vengono ripetute per presentare il "contesto".

(questa è una lunga lista)

a, b = 1, 2                          # simple sequence assignment
a, b = ['green', 'blue']             # list asqignment
a, b = 'XY'                          # string assignment
a, b = range(1,5,2)                  # any iterable will do


                                     # nested sequence assignment

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z' 

(a,b), c = "XYZ"                     # ERROR -- too many values to unpack
(a,b), c = "XY"                      # ERROR -- need more than 1 value to unpack

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'
(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack


                                     # extended sequence unpacking

a, *b = 1,2,3,4,5                    # a = 1, b = [2,3,4,5]
*a, b = 1,2,3,4,5                    # a = [1,2,3,4], b = 5
a, *b, c = 1,2,3,4,5                 # a = 1, b = [2,3,4], c = 5

a, *b = 'X'                          # a = 'X', b = []
*a, b = 'X'                          # a = [], b = 'X'
a, *b, c = "XY"                      # a = 'X', b = [], c = 'Y'
a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

a, b, *c = 1,2,3                     # a = 1, b = 2, c = [3]
a, b, c, *d = 1,2,3                  # a = 1, b = 2, c = 3, d = []

a, *b, c, *d = 1,2,3,4,5             # ERROR -- two starred expressions in assignment

(a,b), c = [1,2],'this'              # a = '1', b = '2', c = 'this'
(a,b), *c = [1,2],'this'             # a = '1', b = '2', c = ['this']

(a,b), c, *d = [1,2],'this'          # a = '1', b = '2', c = 'this', d = []
(a,b), *c, d = [1,2],'this'          # a = '1', b = '2', c = [], d = 'this'

(a,b), (c, *d) = [1,2],'this'        # a = '1', b = '2', c = 't', d = ['h', 'i', 's']

*a = 1                               # ERROR -- target must be in a list or tuple
*a = (1,2)                           # ERROR -- target must be in a list or tuple
*a, = (1,2)                          # a = [1,2]
*a, = 1                              # ERROR -- 'int' object is not iterable
*a, = [1]                            # a = [1]
*a = [1]                             # ERROR -- target must be in a list or tuple
*a, = (1,)                           # a = [1]
*a, = (1)                            # ERROR -- 'int' object is not iterable

*a, b = [1]                          # a = [], b = 1
*a, b = (1,)                         # a = [], b = 1

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
(a,b), *c = 1,2,3                    # ERROR - 'int' object is not iterable
(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]


                                     # extended sequence unpacking -- NESTED

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

*(a,b) = 1,2                         # ERROR -- target must be in a list or tuple
*(a,b), = 1,2                        # a = 1, b = 2

*(a,b) = 'XY'                        # ERROR -- target must be in a list or tuple
*(a,b), = 'XY'                       # a = 'X', b = 'Y'

*(a, b) = 'this'                     # ERROR -- target must be in a list or tuple
*(a, b), = 'this'                    # ERROR -- too many values to unpack
*(a, *b), = 'this'                   # a = 't', b = ['h', 'i', 's']

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

*(a,*b), = 1,2,3,3,4,5,6,7           # a = 1, b = [2, 3, 3, 4, 5, 6, 7]

*(a,*b), *c = 1,2,3,3,4,5,6,7        # ERROR -- two starred expressions in assignment
*(a,*b), (*c,) = 1,2,3,3,4,5,6,7     # ERROR -- 'int' object is not iterable
*(a,*b), c = 1,2,3,3,4,5,6,7         # a = 1, b = [2, 3, 3, 4, 5, 6], c = 7
*(a,*b), (*c,) = 1,2,3,4,5,'XY'      # a = 1, b = [2, 3, 4, 5], c = ['X', 'Y']

*(a,*b), c, d = 1,2,3,3,4,5,6,7      # a = 1, b = [2, 3, 3, 4, 5], c = 6, d = 7
*(a,*b), (c, d) = 1,2,3,3,4,5,6,7    # ERROR -- 'int' object is not iterable
*(a,*b), (*c, d) = 1,2,3,3,4,5,6,7   # ERROR -- 'int' object is not iterable
*(a,*b), *(c, d) = 1,2,3,3,4,5,6,7   # ERROR -- two starred expressions in assignment


*(a,b), c = 'XY', 3                  # ERROR -- need more than 1 value to unpack
*(*a,b), c = 'XY', 3                 # a = [], b = 'XY', c = 3
(a,b), c = 'XY', 3                   # a = 'X', b = 'Y', c = 3

*(a,b), c = 'XY', 3, 4               # a = 'XY', b = 3, c = 4
*(*a,b), c = 'XY', 3, 4              # a = ['XY'], b = 3, c = 4
(a,b), c = 'XY', 3, 4                # ERROR -- too many values to unpack

Come dedurre correttamente a mano il risultato di tali espressioni?


28
Onestamente, la maggior parte di questi sono molto più complicati di quello che vedi nel codice ogni giorno. Impara le basi per spacchettare liste / tuple e starai bene.
Rafe Kettler

2
Nota che questi sono ricorsivi. Quindi, se capisci i primi, puoi gestire tutto. Prova a sostituire, ad esempio, * (* a, b) con * x, scopri cosa x decomprime e poi ricollega (* a, b) per x, ecc.
Peteris

4
@greengit Mi considero una conoscenza avanzata di Python e conosco solo le regole generali :) Non devi conoscere ogni caso d'angolo, a volte devi solo attivare un interprete e testare qualcosa.
Rafe Kettler

Wow, ottima lista. Non sapevo davvero il a, *b = 1, 2, 3tipo di disimballaggio. Ma questo è Py3k giusto?
Niklas R

Risposte:


113

Mi scuso per la lunghezza di questo post, ma ho deciso di optare per la completezza.

Una volta che conosci alcune regole di base, non è difficile generalizzarle. Farò del mio meglio per spiegare con alcuni esempi. Dato che stai parlando di valutarli "a mano", ti suggerirò alcune semplici regole di sostituzione. Fondamentalmente, potresti trovare più facile capire un'espressione se tutti gli iterabili sono formattati allo stesso modo.

Ai soli fini del disimballaggio, le seguenti sostituzioni sono valide sul lato destro di =(cioè per i valori ):

'XY' -> ('X', 'Y')
['X', 'Y'] -> ('X', 'Y')

Se scopri che un valore non viene decompresso, annullerai la sostituzione. (Vedi sotto per ulteriori spiegazioni.)

Inoltre, quando vedi virgole "nude", fingi che ci sia una tupla di primo livello. Fallo sia sul lato sinistro che su quello destro (cioè per lvalues e rvalues ):

'X', 'Y' -> ('X', 'Y')
a, b -> (a, b)

Con queste semplici regole in mente, ecco alcuni esempi:

(a,b), c = "XY", "Z"                 # a = 'X', b = 'Y', c = 'Z'

Applicando le regole di cui sopra, ci convertiamo "XY"a ('X', 'Y'), e coprire le virgole nudi in parentesi:

((a, b), c) = (('X', 'Y'), 'Z')

La corrispondenza visiva qui rende abbastanza ovvio come funziona il compito.

Ecco un esempio errato:

(a,b), c = "XYZ"

Seguendo le regole di sostituzione di cui sopra, otteniamo quanto segue:

((a, b), c) = ('X', 'Y', 'Z')

Questo è chiaramente errato; le strutture annidate non corrispondono. Ora vediamo come funziona per un esempio leggermente più complesso:

(a,b), c, = [1,2],'this'             # a = '1', b = '2', c = 'this'

Applicando le regole di cui sopra, otteniamo

((a, b), c) = ((1, 2), ('t', 'h', 'i', 's'))

Ma ora è chiaro dalla struttura che 'this'non verrà disimballata, ma assegnata direttamente a c. Quindi annulliamo la sostituzione.

((a, b), c) = ((1, 2), 'this')

Ora vediamo cosa succede quando avvolgiamo cin una tupla:

(a,b), (c,) = [1,2],'this'           # ERROR -- too many values to unpack

diventa

((a, b), (c,)) = ((1, 2), ('t', 'h', 'i', 's'))

Anche in questo caso l'errore è ovvio. cnon è più una variabile nuda, ma una variabile all'interno di una sequenza, quindi la sequenza corrispondente a destra viene decompressa in (c,). Ma le sequenze hanno una lunghezza diversa, quindi c'è un errore.

Ora per il disimballaggio esteso utilizzando l' *operatore. Questo è un po 'più complesso, ma è comunque abbastanza semplice. Una variabile preceduta da *diventa un elenco, che contiene tutti gli elementi della sequenza corrispondente che non sono assegnati ai nomi delle variabili. Partendo da un esempio abbastanza semplice:

a, *b, c = "X...Y"                   # a = 'X', b = ['.','.','.'], c = 'Y'

Questo diventa

(a, *b, c) = ('X', '.', '.', '.', 'Y')

Il modo più semplice per analizzarlo è lavorare dalle estremità. 'X'è assegnato a aed 'Y'è assegnato a c. I valori rimanenti nella sequenza vengono inseriti in un elenco e assegnati a b.

I valori come (*a, b)e (a, *b)sono solo casi speciali di cui sopra. Non puoi avere due *operatori all'interno di una sequenza lvalue perché sarebbe ambigua. Dove andrebbero i valori in qualcosa di simile (a, *b, *c, d)- in bo c? Prenderò in considerazione il caso annidato tra un momento.

*a = 1                               # ERROR -- target must be in a list or tuple

Qui l'errore è abbastanza autoesplicativo. Il target ( *a) deve essere in una tupla.

*a, = (1,2)                          # a = [1,2]

Funziona perché c'è una virgola nuda. Applicazione delle regole ...

(*a,) = (1, 2)

Poiché non ci sono variabili diverse da *a, *aassorbe tutti i valori nella sequenza rvalue. Cosa succede se sostituisci il (1, 2)con un singolo valore?

*a, = 1                              # ERROR -- 'int' object is not iterable

diventa

(*a,) = 1

Di nuovo, l'errore qui è autoesplicativo. Non puoi decomprimere qualcosa che non è una sequenza e ha *abisogno di qualcosa da decomprimere. Quindi lo mettiamo in una sequenza

*a, = [1]                            # a = [1]

Che è equivalente a

(*a,) = (1,)

Infine, questo è un punto comune di confusione: (1)è lo stesso di 1- hai bisogno di una virgola per distinguere una tupla da un'istruzione aritmetica.

*a, = (1)                            # ERROR -- 'int' object is not 

Ora per la nidificazione. In realtà questo esempio non era nella tua sezione "NESTED"; forse non ti sei accorto che era annidato?

(a,b), *c = 'XY', 2, 3               # a = 'X', b = 'Y', c = [2,3]

diventa

((a, b), *c) = (('X', 'Y'), 2, 3)

Il primo valore nella tupla di primo livello viene assegnato e i restanti valori nella tupla di primo livello ( 2e 3) vengono assegnati c, proprio come dovremmo aspettarci.

(a,b),c = 1,2,3                      # ERROR -- too many values to unpack
*(a,b), c = 1,2,3                    # a = 1, b = 2, c = 3

Ho già spiegato sopra perché la prima riga genera un errore. La seconda riga è sciocca ma ecco perché funziona:

(*(a, b), c) = (1, 2, 3)

Come spiegato in precedenza, lavoriamo dalle estremità. 3viene assegnato a c, quindi i restanti valori vengono assegnati alla variabile con il *precedente, in questo caso (a, b),. Quindi è equivalente a (a, b) = (1, 2), il che funziona perché ci sono il giusto numero di elementi. Non riesco a pensare a nessun motivo per cui questo potrebbe apparire nel codice funzionante. Allo stesso modo,

*(a, *b), c = 'this'                 # a = 't', b = ['h', 'i'], c = 's'

diventa

(*(a, *b), c) = ('t', 'h', 'i', 's')

Lavorare dalle estremità, 's'è assegnato a ced ('t', 'h', 'i')è assegnato a (a, *b). Lavorando di nuovo dalle estremità, 't'viene assegnato a ae ('h', 'i')viene assegnato a b come elenco. Questo è un altro stupido esempio che non dovrebbe mai apparire nel codice funzionante.


24
Poiché l'OP ha fornito un lungo elenco di esempi, è giusto che tu fornisca un lungo elenco di spiegazioni.
John Y

7

Trovo che la tupla di Python 2 si spacchetti abbastanza semplice. Ogni nome a sinistra corrisponde a un'intera sequenza oa un singolo elemento in una sequenza a destra. Se i nomi corrispondono a singoli elementi di qualsiasi sequenza, devono essere presenti nomi sufficienti per coprire tutti gli elementi.

Il disimballaggio esteso, tuttavia, può certamente creare confusione, perché è così potente. La realtà è che non dovresti mai eseguire gli ultimi 10 o più esempi validi che hai fornito: se i dati sono così strutturati, dovrebbero essere in dictun'istanza di classe o, non in forme non strutturate come elenchi.

Chiaramente, la nuova sintassi può essere abusata. La risposta alla tua domanda è che non dovresti leggere espressioni del genere: sono cattive pratiche e dubito che verranno utilizzate.

Solo perché puoi scrivere espressioni arbitrariamente complesse non significa che dovresti. Potresti scrivere codice come map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))ma non lo fai .


Nota: ho scritto codice del genere, tranne diversi livelli più complessi. Era inteso solo come esercizio e fatto con la piena consapevolezza che dopo tre mesi non avrebbe avuto senso per me e non sarebbe mai stato comprensibile a nessun altro. Se ricordo bene, ha implementato il punto nel test del poligono, ha eseguito alcune trasformazioni di coordinate e ha creato alcuni SVG, HTML e JavaScript.
agf

3

Penso che il tuo codice possa essere fuorviante, usa un'altra forma per esprimerlo.

È come usare parentesi extra nelle espressioni per evitare domande sulla precedenza degli operatori. È sempre un buon investimento per rendere leggibile il tuo codice.

Preferisco utilizzare lo spacchettamento solo per attività semplici come lo scambio.

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.