Rozpakowywanie, rozszerzone rozpakowywanie i zagnieżdżone rozszerzone rozpakowywanie

105

Rozważ następujące wyrażenia. Zwróć uwagę, że niektóre wyrażenia są powtarzane, aby przedstawić „kontekst”.

(to jest długa 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

Jak poprawnie wydedukować ręcznie wynik takich wyrażeń?

treecoder
źródło
28
Szczerze mówiąc, większość z nich jest znacznie bardziej skomplikowana niż to, co widzisz codziennie w kodzie. Naucz się podstaw rozpakowywania list / krotek, a wszystko będzie dobrze.
Rafe Kettler
2
Zauważ, że te są rekurencyjne. Jeśli więc zrozumiesz kilka pierwszych, poradzisz sobie ze wszystkim. Spróbuj zamienić np. * (* A, b) na * x, dowiedz się, jakie x rozpakowuje się, a następnie podłącz (* a, b) z powrotem do x itd.
Peteris
4
@greengit Uważam, że mam zaawansowaną wiedzę o Pythonie i znam tylko ogólne zasady :) Nie musisz znać każdego narożnika, po prostu czasami trzeba odpalić tłumacza i coś przetestować.
Rafe Kettler
Wow, świetna lista. Naprawdę nie wiedziałem o a, *b = 1, 2, 3rozpakowywaniu. Ale to jest Py3k, prawda?
Niklas R

Odpowiedzi:

113

Przepraszam za długość tego posta, ale zdecydowałem się na kompletność.

Kiedy już znasz kilka podstawowych zasad, nietrudno je uogólnić. Postaram się wyjaśnić na kilku przykładach. Ponieważ mówisz o ocenie tych „ręcznie”, zasugeruję kilka prostych zasad zastępowania. Zasadniczo łatwiejsze może być zrozumienie wyrażenia, jeśli wszystkie iteracje są sformatowane w ten sam sposób.

Tylko w celu rozpakowania, następujące podstawienia są ważne po prawej stronie =(tj. Dla wartości r ):

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

Jeśli stwierdzisz, że wartość nie zostanie rozpakowana, cofniesz podstawienie. (Zobacz poniżej, aby uzyskać dalsze wyjaśnienia.)

Gdy zobaczysz „nagie” przecinki, udawaj, że istnieje krotka najwyższego poziomu. Zrób to po lewej i prawej stronie (tj. Dla lvalues i rvalues ):

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

Mając na uwadze te proste zasady, oto kilka przykładów:

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

Stosując powyższe zasady, konwertujemy "XY"na ('X', 'Y')i zakrywamy puste przecinki w parenach:

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

Korespondencja wizualna sprawia, że ​​dość oczywiste jest, jak działa zadanie.

Oto błędny przykład:

(a,b), c = "XYZ"

Postępując zgodnie z powyższymi zasadami zastępowania, otrzymujemy:

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

To jest ewidentnie błędne; zagnieżdżone struktury nie pasują do siebie. Zobaczmy teraz, jak to działa na nieco bardziej złożonym przykładzie:

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

Stosując powyższe zasady otrzymujemy

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

Ale teraz jest to jasne ze struktury, 'this'która nie zostanie rozpakowana, ale przypisana bezpośrednio do c. Więc cofamy podstawienie.

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

Zobaczmy teraz, co się stanie, gdy zawiniemy ckrotkę:

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

Staje się

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

Ponownie błąd jest oczywisty. cnie jest już nagą zmienną, ale zmienną wewnątrz sekwencji, więc odpowiednia sekwencja po prawej stronie jest rozpakowywana do (c,). Ale sekwencje mają inną długość, więc wystąpił błąd.

Teraz do rozszerzonego rozpakowywania za pomocą *operatora. Jest to nieco bardziej złożone, ale nadal jest dość proste. Zmienna poprzedzona *znakiem staje się listą zawierającą elementy z odpowiedniej sekwencji, które nie są przypisane do nazw zmiennych. Zaczynając od dość prostego przykładu:

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

To się stanie

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

Najprostszym sposobem analizy tego jest praca od końca. 'X'jest przypisany do ai 'Y'jest przypisany do c. Pozostałe wartości w sekwencji są umieszczane na liście i przypisywaneb .

Wartości lvalu lubią (*a, b)i (a, *b)są tylko szczególnymi przypadkami powyższego. Nie możesz mieć dwóch *operatorów wewnątrz jednej sekwencji lwartości, ponieważ byłoby to niejednoznaczne. Gdzie wartości pójdą w czymś takim (a, *b, *c, d)- w blub c? Za chwilę rozważę zagnieżdżony przypadek.

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

Tutaj błąd jest dość oczywisty. Element target ( *a) musi znajdować się w krotce.

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

To działa, ponieważ jest nagi przecinek. Stosowanie zasad ...

(*a,) = (1, 2)

Ponieważ nie ma innych zmiennych niż *a, *awysysa wszystkie wartości w sekwencji rwartości. Co się stanie, jeśli zamienisz (1, 2)znak na jedną wartość?

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

staje się

(*a,) = 1

Ponownie, błąd tutaj jest oczywisty. Nie możesz rozpakować czegoś, co nie jest sekwencją i *apotrzebujesz czegoś do rozpakowania. Więc umieściliśmy to w sekwencji

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

Co jest równoważne

(*a,) = (1,)

Wreszcie, jest to powszechny problem: (1)to to samo, co 1- potrzebujesz przecinka, aby odróżnić krotkę od instrukcji arytmetycznej.

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

Teraz do zagnieżdżania. Właściwie tego przykładu nie było w Twojej sekcji „NESTED”; może nie zdawałeś sobie sprawy, że jest zagnieżdżony?

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

Staje się

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

Pierwsza wartość w krotce najwyższego poziomu zostanie przypisana, a pozostałe wartości w krotce najwyższego poziomu ( 2i 3) zostaną przypisane c- tak jak powinniśmy się spodziewać.

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

Wyjaśniłem już powyżej, dlaczego pierwsza linia generuje błąd. Druga linia jest głupia, ale oto dlaczego to działa:

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

Jak wyjaśniono wcześniej, pracujemy od końca. 3jest przypisany c, a następnie pozostałe wartości są przypisane do zmiennej o *poprzedzających, w tym przypadku (a, b). Więc to jest równoważne (a, b) = (1, 2), co tak się składa, że ​​działa, ponieważ istnieje odpowiednia liczba elementów. Nie przychodzi mi do głowy żaden powód, dla którego mogłoby to kiedykolwiek pojawić się w działającym kodzie. Podobnie,

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

staje się

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

Praca od końca, 's'jest przypisana ci ('t', 'h', 'i')jest przypisana (a, *b). Pracuje ponownie od końca, 't'jest przypisany do ai ('h', 'i')jest przypisany do b jako lista. To kolejny głupi przykład, który nigdy nie powinien pojawić się w działającym kodzie.

nadawca
źródło
24
Ponieważ PO podał długą listę przykładów, dobrze jest tylko podać długą listę wyjaśnień.
John Y
7

Uważam, że rozpakowywanie krotki Pythona 2 jest całkiem proste. Każda nazwa po lewej stronie odpowiada całej sekwencji lub pojedynczemu elementowi w sekwencji po prawej stronie. Jeśli nazwy odpowiadają pojedynczym elementom dowolnej sekwencji, musi istnieć wystarczająca liczba nazw, aby objąć wszystkie elementy.

Jednak wydłużone rozpakowywanie może z pewnością być mylące, ponieważ jest tak potężne. W rzeczywistości nigdy nie powinieneś robić ostatnich 10 lub więcej ważnych przykładów, które podałeś - jeśli dane są tak ustrukturyzowane, powinnydict instancji klasy lub klasy, a nie w nieustrukturyzowanych formularzach, takich jak listy.

Oczywiście nowa składnia może być nadużywana. Odpowiedź na twoje pytanie brzmi: nie powinieneś musieć czytać takich wyrażeń - to zła praktyka i wątpię, czy zostaną użyte.

To, że możesz pisać dowolnie złożone wyrażenia, nie oznacza, że ​​powinieneś. Możesz napisać kod, map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))ale tego nie zrobisz .

agf
źródło
Uwaga: napisałem taki kod, z wyjątkiem kilku poziomów bardziej złożonych. Miało to być tylko ćwiczenie i wykonane z pełną świadomością, że po trzech miesiącach będzie to dla mnie bez znaczenia i nigdy nie będzie zrozumiałe dla nikogo innego. Jeśli dobrze pamiętam, zaimplementował punkt w teście wielokąta, dokonał transformacji współrzędnych i stworzył kilka SVG, HTML i JavaScript.
agf
3

Myślę, że twój kod może wprowadzać w błąd, użyj innej formy, aby to wyrazić.

To tak, jakby używać dodatkowych nawiasów w wyrażeniach, aby uniknąć pytań o pierwszeństwo operatorów. To zawsze dobra inwestycja, aby Twój kod był czytelny.

Wolę używać rozpakowywania tylko do prostych zadań, takich jak zamiana.

Michał Šrajer
źródło