Auspacken, erweitertes Auspacken und verschachteltes erweitertes Auspacken

104

Betrachten Sie die folgenden Ausdrücke. Beachten Sie, dass einige Ausdrücke wiederholt werden, um den "Kontext" darzustellen.

(Dies ist eine lange Liste)

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

Wie kann man das Ergebnis solcher Ausdrücke von Hand richtig ableiten?

Baumkodierer
quelle
28
Ehrlich gesagt sind die meisten davon weitaus komplizierter als das, was Sie jeden Tag im Code sehen. Lernen Sie die Grundlagen des Auspackens von Listen / Tupeln und Sie werden in Ordnung sein.
Rafe Kettler
2
Beachten Sie, dass diese rekursiv sind. Wenn Sie also die ersten paar verstehen, können Sie mit allem umgehen. Versuchen Sie, z. B. * (* a, b) durch * x zu ersetzen, finden Sie heraus, was x auspackt, und schließen Sie dann (* a, b) wieder an x ​​usw. an
Peteris
4
@greengit Ich halte mich für fortgeschrittene Python-Kenntnisse und kenne nur die allgemeinen Regeln :) Man muss nicht jeden Eckfall kennen, man muss nur manchmal einen Interpreter starten und etwas testen.
Rafe Kettler
Wow tolle Liste. Ich wusste wirklich nichts über die a, *b = 1, 2, 3Art des Auspackens. Aber das ist Py3k, oder?
Niklas R

Antworten:

113

Ich entschuldige mich für die Länge dieses Beitrags, aber ich habe mich für die Vollständigkeit entschieden.

Sobald Sie einige Grundregeln kennen, ist es nicht schwer, sie zu verallgemeinern. Ich werde mein Bestes geben, um dies anhand einiger Beispiele zu erklären. Da Sie über die Bewertung dieser "von Hand" sprechen, schlage ich einige einfache Substitutionsregeln vor. Grundsätzlich fällt es Ihnen möglicherweise leichter, einen Ausdruck zu verstehen, wenn alle iterablen Elemente auf dieselbe Weise formatiert sind.

Nur zum Auspacken gelten die folgenden Ersetzungen auf der rechten Seite von =(dh für rWerte ):

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

Wenn Sie feststellen, dass ein Wert nicht entpackt wird, machen Sie die Ersetzung rückgängig. (Weitere Erklärungen finden Sie weiter unten.)

Wenn Sie "nackte" Kommas sehen, tun Sie so, als gäbe es ein Tupel der obersten Ebene. Tun Sie dies sowohl auf der linken als auch auf der rechten Seite (dh für lWerte und rWerte ):

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

In Anbetracht dieser einfachen Regeln sind hier einige Beispiele:

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

Unter Anwendung der oben genannten Regeln wandeln wir "XY"auf ('X', 'Y'), und decken die nackten Kommas in Pars:

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

Die visuelle Entsprechung hier macht ziemlich deutlich, wie die Zuordnung funktioniert.

Hier ist ein fehlerhaftes Beispiel:

(a,b), c = "XYZ"

Nach den oben genannten Substitutionsregeln erhalten wir Folgendes:

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

Dies ist eindeutig falsch; Die verschachtelten Strukturen stimmen nicht überein. Nun wollen wir sehen, wie es für ein etwas komplexeres Beispiel funktioniert:

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

Unter Anwendung der oben genannten Regeln erhalten wir

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

Aber jetzt geht aus der Struktur hervor, dass 'this'sie nicht entpackt, sondern direkt zugewiesen wird c. Also machen wir die Substitution rückgängig.

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

Nun wollen wir sehen, was passiert, wenn wir cein Tupel einpacken:

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

Wird

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

Auch hier ist der Fehler offensichtlich. cist keine nackte Variable mehr, sondern eine Variable innerhalb einer Sequenz, und so wird die entsprechende Sequenz rechts entpackt (c,). Die Sequenzen haben jedoch eine andere Länge, sodass ein Fehler auftritt.

Jetzt zum erweiterten Auspacken mit dem *Bediener. Das ist etwas komplexer, aber immer noch ziemlich einfach. Eine vorangestellte Variable wird *zu einer Liste, die Elemente aus der entsprechenden Sequenz enthält, die nicht den Variablennamen zugewiesen sind. Beginnen wir mit einem ziemlich einfachen Beispiel:

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

Das wird

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

Der einfachste Weg, dies zu analysieren, besteht darin, von den Enden aus zu arbeiten. 'X'ist zugeordnet aund 'Y'ist zugeordnet c. Die restlichen Werte in der Sequenz werden in eine Liste aufgenommen und zugewiesen b.

L-Werte mögen (*a, b)und (a, *b)sind nur Sonderfälle der oben genannten. Sie können nicht zwei *Operatoren in einer lvalue-Sequenz haben, da dies nicht eindeutig wäre. Wohin würden die Werte in so etwas gehen (a, *b, *c, d)- in boder c? Ich werde gleich auf den verschachtelten Fall eingehen.

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

Hier ist der Fehler ziemlich selbsterklärend. Das Ziel ( *a) muss sich in einem Tupel befinden.

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

Dies funktioniert, weil es ein nacktes Komma gibt. Regeln anwenden ...

(*a,) = (1, 2)

Da es keine anderen Variablen als gibt *a, *awerden alle Werte in der rvalue-Sequenz verschluckt. Was ist, wenn Sie das (1, 2)durch einen einzelnen Wert ersetzen ?

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

wird

(*a,) = 1

Auch hier ist der Fehler selbsterklärend. Sie können etwas nicht auspacken, das keine Sequenz ist und *azum Auspacken etwas benötigt. Also setzen wir es in eine Reihenfolge

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

Welches ist äquivalent zu

(*a,) = (1,)

Schließlich ist dies ein häufiger Punkt der Verwirrung: Es (1)ist dasselbe wie 1- Sie benötigen ein Komma, um ein Tupel von einer arithmetischen Aussage zu unterscheiden.

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

Nun zum Verschachteln. Eigentlich war dieses Beispiel nicht in Ihrem Abschnitt "NESTED"; Vielleicht haben Sie nicht bemerkt, dass es verschachtelt ist?

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

Wird

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

Der erste Wert im Tupel der obersten Ebene wird zugewiesen, und die verbleibenden Werte im Tupel der obersten Ebene ( 2und 3) werden zugewiesen c- genau wie wir es erwarten sollten.

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

Ich habe oben bereits erklärt, warum die erste Zeile einen Fehler auslöst. Die zweite Zeile ist albern, aber hier ist, warum es funktioniert:

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

Wie bereits erläutert, arbeiten wir von Anfang an. 3wird zugewiesen c, und dann werden die verbleibenden Werte der Variablen mit der *vorhergehenden zugewiesen , in diesem Fall (a, b). Das entspricht also (a, b) = (1, 2), was zufällig funktioniert, weil es die richtige Anzahl von Elementen gibt. Ich kann mir keinen Grund vorstellen, warum dies jemals im Arbeitscode erscheinen würde. Ähnlich,

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

wird

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

Arbeiten von den Enden, 's'ist zugeordnet cund ('t', 'h', 'i')zugeordnet (a, *b). Von den Enden aus wieder arbeiten, 't'wird zugewiesen aund ('h', 'i')wird b als Liste zugewiesen. Dies ist ein weiteres dummes Beispiel, das niemals im Arbeitscode erscheinen sollte.

senderle
quelle
24
Da das OP Beispiele für eine lange Liste gegeben hat, ist es nur passend, dass Sie eine lange Liste von Erklärungen geben.
John Y
7

Ich finde das Auspacken des Python 2-Tupels ziemlich einfach. Jeder Name links entspricht entweder einer ganzen Sequenz oder einem einzelnen Element in einer Sequenz rechts. Wenn Namen einzelnen Elementen einer beliebigen Sequenz entsprechen, müssen genügend Namen vorhanden sein, um alle Elemente abzudecken.

Ein längeres Auspacken kann jedoch sicherlich verwirrend sein, da es so leistungsstark ist. Die Realität ist, dass Sie niemals die letzten 10 oder mehr gültigen Beispiele machen sollten, die Sie angegeben haben. Wenn die Daten so strukturiert sind, sollten sie sich in einer dictoder einer Klasseninstanz befinden und nicht in unstrukturierten Formen wie Listen.

Natürlich kann die neue Syntax missbraucht werden. Die Antwort auf Ihre Frage lautet, dass Sie solche Ausdrücke nicht lesen müssen - sie sind eine schlechte Praxis, und ich bezweifle, dass sie verwendet werden.

Nur weil Sie beliebig komplexe Ausdrücke schreiben können, heißt das nicht, dass Sie es sollten. Sie könnten Code wie schreiben, map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))aber Sie tun es nicht .

agf
quelle
Hinweis: Ich habe solchen Code geschrieben, mit Ausnahme einiger komplexerer Ebenen. Es war nur als Übung gedacht und wurde mit vollem Wissen durchgeführt, dass es nach drei Monaten für mich bedeutungslos und für andere niemals verständlich sein würde . Wenn ich mich recht erinnere, hat es den Punkt im Polygontest implementiert, einige Koordinatentransformationen durchgeführt und einige SVGs, HTML und JavaScript erstellt.
Agf
3

Ich denke, Ihr Code kann irreführend sein. Verwenden Sie ein anderes Formular, um ihn auszudrücken.

Es ist so, als würden Sie zusätzliche Klammern in Ausdrücken verwenden, um Fragen zur Priorität von Operatoren zu vermeiden. Es ist immer eine gute Investition, Ihren Code lesbar zu machen.

Ich bevorzuge das Auspacken nur für einfache Aufgaben wie das Tauschen.

Michał Šrajer
quelle