Desempacotamento, desempacotamento estendido e desempacotamento estendido aninhado

105

Considere as seguintes expressões. Observe que algumas expressões são repetidas para apresentar o "contexto".

(esta é uma longa 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

Como deduzir corretamente o resultado de tais expressões manualmente?

treecoder
fonte
28
Honestamente, a maioria deles é muito mais complicada do que o que você vê no código todos os dias. Aprenda o básico para descompactar listas / tuplas e você ficará bem.
Rafe Kettler
2
Observe que eles são recursivos. Portanto, se você entender os primeiros, poderá cuidar de tudo. Tente substituir, por exemplo, * (* a, b) por * x, descubra o que x descompacta e conecte (* a, b) de volta para x, etc.
Peteris
4
@greengit Eu me considero ter um conhecimento avançado de Python e apenas conheço as regras gerais :) Você não precisa saber todos os casos esquivos, apenas às vezes você precisa iniciar um interpretador e testar algo.
Rafe Kettler
Ótima lista. Eu realmente não sabia sobre o a, *b = 1, 2, 3tipo de desempacotamento. Mas isso é Py3k certo?
Niklas R

Respostas:

113

Minhas desculpas pela extensão deste post, mas decidi optar pela completude.

Depois de conhecer algumas regras básicas, não é difícil generalizá-las. Vou fazer o meu melhor para explicar com alguns exemplos. Já que você está falando sobre avaliá-los "manualmente", vou sugerir algumas regras de substituição simples. Basicamente, você pode achar mais fácil entender uma expressão se todos os iteráveis ​​estiverem formatados da mesma maneira.

Apenas para fins de desempacotamento, as seguintes substituições são válidas no lado direito do =(ou seja, para rvalues ):

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

Se você descobrir que um valor não foi descompactado, você desfará a substituição. (Veja abaixo para mais explicações.)

Além disso, quando vir vírgulas "nuas", finja que há uma tupla de nível superior. Faça isso em ambos à esquerda e à direita (ou seja, para lvalues e rvalues ):

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

Com essas regras simples em mente, aqui estão alguns exemplos:

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

Aplicando as regras acima, nós convertemos "XY"a ('X', 'Y'), e cobrir as vírgulas nuas em parênteses:

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

A correspondência visual aqui torna bastante óbvio como a tarefa funciona.

Aqui está um exemplo errado:

(a,b), c = "XYZ"

Seguindo as regras de substituição acima, temos o seguinte:

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

Isso é claramente errado; as estruturas aninhadas não combinam. Agora vamos ver como funciona para um exemplo um pouco mais complexo:

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

Aplicando as regras acima, obtemos

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

Mas agora está claro pela estrutura que 'this'não será desempacotado, mas atribuído diretamente a c. Portanto, desfazemos a substituição.

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

Agora vamos ver o que acontece quando envolvemos cuma tupla:

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

Torna-se

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

Novamente, o erro é óbvio. cnão é mais uma variável simples, mas uma variável dentro de uma sequência e, portanto, a sequência correspondente à direita é descompactada em (c,). Mas as sequências têm um comprimento diferente, então há um erro.

Agora, para desempacotamento prolongado usando o *operador. Isso é um pouco mais complexo, mas ainda é bastante simples. Uma variável precedida por *torna-se uma lista, que contém todos os itens da sequência correspondente que não foram atribuídos a nomes de variáveis. Começando com um exemplo bastante simples:

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

Isso se torna

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

A maneira mais simples de analisar isso é trabalhar a partir das extremidades. 'X'é atribuído a ae 'Y'é atribuído a c. Os valores restantes na sequência são colocados em uma lista e atribuídos a eles b.

Valores como (*a, b)e (a, *b)são apenas casos especiais dos anteriores. Você não pode ter dois *operadores dentro de uma sequência lvalue porque seria ambíguo. Para onde iriam os valores em algo assim (a, *b, *c, d)- em bou c? Vou considerar o caso aninhado em um momento.

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

Aqui, o erro é bastante autoexplicativo. O target ( *a) deve estar em uma tupla.

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

Isso funciona porque há uma vírgula nua. Aplicando as regras ...

(*a,) = (1, 2)

Uma vez que não há variáveis ​​além de *a, *aengole todos os valores na sequência rvalue. E se você substituir o (1, 2)por um único valor?

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

torna-se

(*a,) = 1

Novamente, o erro aqui é autoexplicativo. Você não pode desempacotar algo que não seja uma sequência e *aprecisa de algo para desempacotar. Então, colocamos em uma sequência

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

Qual é equivalente a

(*a,) = (1,)

Finalmente, este é um ponto comum de confusão: (1)é o mesmo que 1- você precisa de uma vírgula para distinguir uma tupla de uma instrução aritmética.

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

Agora, para o aninhamento. Na verdade, este exemplo não estava em sua seção "ANINHADA"; talvez você não tenha percebido que estava aninhado?

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

Torna-se

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

O primeiro valor na tupla de nível superior é atribuído, e os valores restantes na tupla de nível superior ( 2e 3) são atribuídos c- exatamente como deveríamos esperar.

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

Já expliquei acima porque a primeira linha gera um erro. A segunda linha é boba, mas aqui está porque funciona:

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

Conforme explicado anteriormente, trabalhamos desde o início. 3é atribuído a ce, em seguida, os valores restantes são atribuídos à variável *precedente, neste caso (a, b),. Isso é equivalente a (a, b) = (1, 2), o que funciona porque há o número certo de elementos. Não consigo pensar em nenhum motivo para isso aparecer no código funcional. Similarmente,

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

torna-se

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

Trabalhar a partir das pontas, 's'é atribuído a ce ('t', 'h', 'i')é atribuído a (a, *b). Trabalhando novamente a partir das extremidades, 't'é atribuído a ae ('h', 'i')é atribuído a b como uma lista. Este é outro exemplo bobo que nunca deveria aparecer no código funcional.

remetente
fonte
24
Visto que o OP deu uma longa lista de exemplos, convém que você dê uma longa lista de explicações.
John Y
7

Acho a tupla do Python 2 descompactada muito simples. Cada nome à esquerda corresponde a uma sequência inteira ou a um único item em uma sequência à direita. Se os nomes corresponderem a itens únicos de qualquer sequência, deve haver nomes suficientes para cobrir todos os itens.

A descompactação prolongada, entretanto, pode certamente ser confusa, porque é muito poderosa. A realidade é que você nunca deve fazer os últimos 10 ou mais exemplos válidos que forneceu - se os dados forem assim estruturados, eles devem estar em uma dictou uma instância de classe, e não em formas não estruturadas como listas.

Claramente, a nova sintaxe pode ser abusada. A resposta à sua pergunta é que você não deveria ter que ler expressões como essa - são uma má prática e duvido que sejam usadas.

Só porque você pode escrever expressões arbitrariamente complexas, não significa que você deve. Você poderia escrever código como, map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))mas não o faz .

agf
fonte
Nota: Eu escrevi um código assim, exceto vários níveis mais complexos. Foi planejado apenas como um exercício, e feito com pleno conhecimento de que depois de três meses não teria sentido para mim e nunca seria compreensível para ninguém. Se bem me lembro, ele implementou o teste de ponto no polígono, fez algumas transformações de coordenadas e criou alguns SVGs, HTML e JavaScript.
agf
3

Eu acho que seu código pode ser enganoso, use outra forma para expressá-lo.

É como usar colchetes extras em expressões para evitar questões sobre a precedência dos operadores. É sempre um bom investimento para tornar seu código legível.

Eu prefiro usar a descompactação apenas para tarefas simples como a troca.

Michał Šrajer
fonte