Desembalaje, desembalaje ampliado y desembalaje ampliado anidado

105

Considere las siguientes expresiones. Tenga en cuenta que algunas expresiones se repiten para presentar el "contexto".

(esta es una lista larga)

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

¿Cómo deducir correctamente el resultado de tales expresiones a mano?

codificador de árbol
fuente
28
Honestamente, la mayoría de estos son mucho más complicados de lo que ves en el código todos los días. Aprenda los conceptos básicos de descomprimir listas / tuplas y estará bien.
Rafe Kettler
2
Tenga en cuenta que estos son recursivos. Entonces, si entiendes y los primeros, puedes manejar todo. Intente reemplazar, por ejemplo, * (* a, b) con * x, averigüe qué desempaqueta x y luego conecte (* a, b) nuevamente para x, etc.
Peteris
4
@greengit Considero que tengo un conocimiento avanzado de Python y solo conozco las reglas generales :) No es necesario que conozcas todos los casos, solo que a veces necesitas activar un intérprete y probar algo.
Rafe Kettler
Guau gran lista. Realmente no sabía sobre el a, *b = 1, 2, 3tipo de desembalaje. Pero esto es Py3k, ¿verdad?
Niklas R

Respuestas:

113

Mis disculpas por la extensión de esta publicación, pero decidí optar por la integridad.

Una vez que conozca algunas reglas básicas, no es difícil generalizarlas. Haré todo lo posible para explicarlo con algunos ejemplos. Ya que está hablando de evaluar estos "a mano", sugeriré algunas reglas de sustitución simples. Básicamente, es posible que le resulte más fácil comprender una expresión si todos los iterables tienen el mismo formato.

Solo con el propósito de desempacar, las siguientes sustituciones son válidas en el lado derecho de =(es decir, para rvalues ):

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

Si encuentra que un valor no se descomprime, deshará la sustitución. (Consulte a continuación para obtener más explicaciones).

Además, cuando veas comas "desnudas", finge que hay una tupla de nivel superior. Haga esto en ambos lados izquierdo y el lado derecho (es decir, para lvalues y rvalues ):

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

Con esas simples reglas en mente, aquí hay algunos ejemplos:

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

La aplicación de las reglas anteriores, convertimos "XY"a ('X', 'Y'), y cubrimos las comas desnudas en parens:

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

La correspondencia visual aquí hace que sea bastante obvio cómo funciona la tarea.

Aquí tienes un ejemplo erróneo:

(a,b), c = "XYZ"

Siguiendo las reglas de sustitución anteriores, obtenemos lo siguiente:

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

Esto es claramente erróneo; las estructuras anidadas no coinciden. Ahora veamos cómo funciona para un ejemplo un poco más complejo:

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

Aplicando las reglas anteriores, obtenemos

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

Pero ahora está claro por la estructura que 'this'no se descomprimirá, sino que se asignará directamente a c. Entonces deshacemos la sustitución.

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

Ahora veamos qué sucede cuando ajustamos cuna tupla:

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

Se convierte

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

Una vez más, el error es obvio. cya no es una variable desnuda, sino una variable dentro de una secuencia, por lo que la secuencia correspondiente a la derecha se descomprime en (c,). Pero las secuencias tienen una longitud diferente, por lo que hay un error.

Ahora para un desembalaje prolongado con el *operador. Esto es un poco más complejo, pero sigue siendo bastante sencillo. Una variable precedida por se *convierte en una lista, que contiene los elementos de la secuencia correspondiente que no están asignados a los nombres de las variables. Comenzando con un ejemplo bastante simple:

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

Esto se convierte en

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

La forma más sencilla de analizar esto es trabajar desde los extremos. 'X'está asignado ay 'Y'está asignado a c. Los valores restantes de la secuencia se colocan en una lista y se asignan ab .

Los valores L como (*a, b)y (a, *b)son solo casos especiales de lo anterior. No puede tener dos *operadores dentro de una secuencia lvalue porque sería ambiguo. ¿Dónde irían los valores en algo como esto (a, *b, *c, d), en bo c? Consideraré el caso anidado en un momento.

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

Aquí el error se explica por sí mismo. El objetivo ( *a) debe estar en una tupla.

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

Esto funciona porque hay una coma desnuda. Aplicando las reglas ...

(*a,) = (1, 2)

Dado que no hay otras variables que no sean *a, *aabsorbe todos los valores en la secuencia rvalue. ¿Qué pasa si reemplaza el (1, 2)con un valor único?

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

se convierte en

(*a,) = 1

Una vez más, el error aquí se explica por sí mismo. No puede descomprimir algo que no es una secuencia y *anecesita algo para desempaquetar. Entonces lo ponemos en una secuencia

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

Que es equivalente a

(*a,) = (1,)

Finalmente, este es un punto común de confusión: (1)es lo mismo que 1: necesita una coma para distinguir una tupla de una declaración aritmética.

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

Ahora para anidar. En realidad, este ejemplo no estaba en su sección "NESTED"; tal vez no te diste cuenta de que estaba anidado?

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

Se convierte

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

Se asigna el primer valor en la tupla de nivel superior, y los valores restantes en la tupla de nivel superior ( 2y 3) se asignan c, tal como deberí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

Ya he explicado anteriormente por qué la primera línea arroja un error. La segunda línea es tonta, pero aquí está la razón por la que funciona:

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

Como se explicó anteriormente, trabajamos desde los extremos. 3se asigna a c, y luego los valores restantes se asignan a la variable con el *anterior, en este caso (a, b),. Entonces eso es equivalente a (a, b) = (1, 2), que funciona porque hay el número correcto de elementos. No puedo pensar en ninguna razón por la que esto aparezca en el código de trabajo. Similar,

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

se convierte en

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

Trabajar desde los extremos, 's'se asigna cy ('t', 'h', 'i')se asigna a (a, *b). Trabajando de nuevo desde los extremos, 't'se asigna ay ('h', 'i')se asigna a b como una lista. Este es otro ejemplo tonto que nunca debería aparecer en el código de trabajo.

remitente
fuente
24
Dado que el OP dio una larga lista de ejemplos, es lógico que proporcione una larga lista de explicaciones.
John Y
7

Encuentro el desempaquetado de tuplas de Python 2 bastante sencillo. Cada nombre de la izquierda se corresponde con una secuencia completa o con un solo elemento en una secuencia de la derecha. Si los nombres corresponden a elementos individuales de cualquier secuencia, entonces debe haber suficientes nombres para cubrir todos los elementos.

Sin embargo, el desembalaje prolongado puede resultar confuso porque es muy poderoso. La realidad es que nunca debería estar haciendo los últimos 10 o más ejemplos válidos que dio; si los datos están así de estructurados, deberían estar en una dictinstancia de clase o, no en formularios no estructurados como listas.

Claramente, se puede abusar de la nueva sintaxis. La respuesta a su pregunta es que no debería tener que leer expresiones como esa; son una mala práctica y dudo que se utilicen.

El hecho de que pueda escribir expresiones arbitrariamente complejas no significa que deba hacerlo. Podrías escribir código comomap(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables)) pero no lo haces .

agf
fuente
Nota: He escrito un código así, excepto varios niveles más complejos. Estaba pensado sólo como un ejercicio, y se hizo con pleno conocimiento de que después de tres meses no tendría sentido para mí y nunca sería comprensible para nadie más. Si mal no recuerdo, implementó la prueba de punto en polígono, hizo algunas transformaciones de coordenadas y diseñó algunos SVG, HTML y JavaScript.
2011
3

Creo que su código puede ser engañoso, use otra forma para expresarlo.

Es como usar corchetes adicionales en expresiones para evitar preguntas sobre la precedencia de los operadores. Siempre es una buena inversión hacer que su código sea legible.

Prefiero usar el desempaquetado solo para tareas simples como intercambiar.

Michał Šrajer
fuente