Frage Entpacken, erweitertes Entpacken und verschachteltes erweitertes Entpacken


Betrachten Sie diese Ausdrücke ... Bitte haben Sie Geduld ... das ist eine lange Liste ...

(Anmerkung: Einige Ausdrücke werden wiederholt - dies ist nur um einen "Kontext" darzustellen)

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 verstehst du solche Komplexität und Verwirrung? Wie man immer RECHTS sein kann, wenn man die Ergebnisse solcher Ausdrücke von Hand berechnet. Oder sollte ich, wenn ich den Code einer anderen Person lese, sie einfach ignorieren und niemals versuchen zu ergründen, was der Ausdruck tatsächlich macht?


75
2017-08-06 14:59


Ursprung


Antworten:


Ich entschuldige mich für die Länge dieses Posts, aber ich entschied mich für die Vollständigkeit zu entscheiden.

Sobald Sie ein paar grundlegende Regeln kennen, ist es nicht schwer, sie zu verallgemeinern. Ich werde mein Bestes geben, um ein paar Beispiele zu erklären. Da Sie davon sprechen, diese "von Hand" zu bewerten, werde ich einige einfache Substitutionsregeln vorschlagen. Im Grunde genommen ist es vielleicht einfacher, einen Ausdruck zu verstehen, wenn alle Iterables auf die gleiche Weise formatiert sind.

Nur für die Zwecke des Entpackens gelten die folgenden Ersetzungen auf der rechten Seite des = (d. h. für rvalues):

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

Wenn Sie feststellen, dass ein Wert nicht ausgepackt wird, machen Sie die Substitution rückgängig. (Siehe unten für weitere Erklärungen.)

Wenn Sie "leere" Kommas sehen, tun Sie so, als gäbe es ein Top-Level-Tupel. Tun Sie dies sowohl auf der linken als auch auf der rechten Seite (d.h. für Werte und rvalues):

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

Unter Berücksichtigung dieser einfachen Regeln finden Sie hier einige Beispiele:

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

Unter Anwendung der obigen Regeln konvertieren wir "XY" zu ('X', 'Y')und bedecke die nackten Kommata in Parens:

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

Die visuelle Korrespondenz macht es ziemlich offensichtlich, wie die Aufgabe funktioniert.

Hier ist ein fehlerhaftes Beispiel:

(a,b), c = "XYZ"

Nach den obigen Substitutionsregeln erhalten wir folgendes:

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

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

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

Unter Anwendung der obigen Regeln erhalten wir

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

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

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

Jetzt sehen wir, was passiert, wenn wir wickeln c in einem Tupel:

(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. c ist nicht mehr eine nackte Variable, sondern eine Variable innerhalb einer Sequenz, und so wird die entsprechende Sequenz rechts entpackt (c,). Aber die Sequenzen haben eine andere Länge, daher gibt es einen Fehler.

Jetzt zum längeren Entpacken mit dem * Operator. Das ist ein bisschen komplexer, aber es ist immer noch ziemlich einfach. Eine Variable mit vorangestelltem * wird zu einer Liste, die alle Elemente aus der entsprechenden Sequenz enthält, die keinem Variablennamen zugeordnet sind. Angefangen 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, ist von den Enden zu arbeiten. 'X' ist zugeordnet a und 'Y' ist zugeordnet c. Die restlichen Werte in der Sequenz werden in eine Liste eingefügt und zugewiesen b.

Lvalues ​​mögen (*a, b) und (a, *b) sind nur spezielle Fälle des oben genannten. Sie können nicht zwei haben *Operatoren in einer Lvalue-Sequenz, weil sie mehrdeutig wäre. Wo würden die Werte in etwa so aussehen? (a, *b, *c, d) -- im b oder c? Ich werde den verschachtelten Fall gleich betrachten.

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

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

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

Das funktioniert, weil es ein nacktes Komma gibt. Die Regeln anwenden ...

(*a,) = (1, 2)

Da gibt es keine anderen Variablen als *a, *a schlürft alle Werte in der rvalue-Sequenz auf. Was ist, wenn Sie das ersetzen? (1, 2) mit einem einzigen Wert?

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

wird

(*a,) = 1

Auch hier ist der Fehler selbsterklärend. Du kannst nichts entpacken, das keine Sequenz ist, und *a braucht etwas zum Auspacken. Also haben wir es in eine Reihenfolge gebracht

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

Welches ist äquivalent zu

(*a,) = (1,)

Schließlich ist dies ein gemeinsamer Punkt der Verwirrung: (1) ist das gleiche wie 1 - Sie benötigen ein Komma, um ein Tupel von einer arithmetischen Aussage zu unterscheiden.

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

Jetzt zum Verschachteln. Eigentlich war dieses Beispiel nicht in Ihrem Abschnitt "NESTED"; vielleicht hast du nicht bemerkt, dass es verschachtelt war?

(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 restlichen Werte im Tupel der obersten Ebene (2 und 3) sind zugeordnet c - so 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 schon erklärt warum die erste Zeile einen Fehler verursacht. Die zweite Zeile ist albern, aber hier ist, warum es funktioniert:

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

Wie zuvor erklärt, arbeiten wir von den Enden. 3 ist zugeordnet c, und dann werden die übrigen Werte der Variablen mit dem zugewiesen * davor, in diesem Fall, (a, b). Das entspricht also (a, b) = (1, 2), was passiert, weil es die richtige Anzahl von Elementen gibt. Ich kann mir keinen Grund vorstellen, warum dies jemals im Arbeitscode auftauchen 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 c, und ('t', 'h', 'i') ist zugeordnet (a, *b). Wieder von den Enden arbeiten, 't' ist zugeordnet a, und ('h', 'i') wird b als Liste zugewiesen. Dies ist ein weiteres dummes Beispiel, das niemals im Arbeitscode erscheinen sollte.


87
2017-08-06 17:30



Ich finde das Python 2-Tupel-Entpacken ziemlich einfach. Jeder Name auf der linken Seite entspricht entweder einer ganzen Sequenz oder einem einzelnen Element in einer Sequenz auf der rechten Seite. Wenn Namen einzelnen Elementen einer Sequenz entsprechen, müssen genügend Namen vorhanden sein, um alle Elemente abzudecken.

Erweitertes Entpacken kann jedoch sicherlich verwirrend sein, weil es so mächtig ist. Die Realität ist, dass Sie nie die letzten 10 oder mehr gültigen Beispiele, die Sie gaben, machen sollten - wenn die Daten so strukturiert sind, sollten sie in einem sein dict oder eine Klasseninstanz, nicht unstrukturierte Formen wie Listen.

Natürlich kann die neue Syntax missbraucht werden. Die Antwort auf Ihre Frage ist, dass Sie sollte nicht Ich muss solche Ausdrücke lesen - sie sind schlecht und ich bezweifle, dass sie benutzt werden.

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


6
2017-08-06 15:13



Ich denke, dass Ihr Code irreführend sein kann, verwenden Sie eine andere Form, um es auszudrücken.

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

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


2
2017-08-06 15:08