Pregunta Desembalaje, desempaquetado extendido y desembalaje extendido anidado


Considere estas expresiones ... Por favor, sean pacientes ... esta es una lista larga ...

(Nota: algunas expresiones se repiten, esto es solo para presentar un "contexto")

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 comprendes tal complejidad y confusión? Cómo uno siempre puede estar CORRECTO al calcular los resultados de tales expresiones a mano. O, al leer el código de otra persona, ¿debería simplemente ignorarlos y nunca tratar de entender qué está haciendo realmente la expresión?


75
2017-08-06 14:59


origen


Respuestas:


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

Una vez que conoces algunas reglas básicas, no es difícil generalizarlas. Haré todo lo posible para explicar con algunos ejemplos. Ya que está hablando de evaluar estos "a mano", sugeriré algunas reglas de sustitución simples. Básicamente, puede que le resulte más fácil entender una expresión si todos los iterables están formateados de la misma manera.

Para los propósitos de desempaquetar solamente, las siguientes sustituciones son válidas en el lado derecho de la = (es decir, para valores)

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

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

Además, cuando ve comas "desnudas", imagina que hay una tupla de nivel superior. Haga esto tanto en el lado izquierdo como en el derecho (es decir, lvalues y valores)

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

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

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

Aplicando las reglas anteriores, convertimos "XY" a ('X', 'Y')y cubre las comas desnudas en parens:

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

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

Aquí hay 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 desde la estructura que 'this' no se desempaquetará, sino que se asignará directamente a c. Así que deshacemos la sustitución.

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

Ahora veamos qué sucede cuando envolvemos c en una tupla:

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

Se convierte

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

De nuevo, el error es obvio. c ya no es una variable desnuda, sino una variable dentro de una secuencia, por lo que la secuencia correspondiente de la derecha se desempaqueta en (c,). Pero las secuencias tienen una longitud diferente, por lo que hay un error.

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

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

Esto se convierte

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

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

Lvalues ​​como (*a, b) y (a, *b) son solo casos especiales de lo anterior. No puedes 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 b o c? Consideraré el caso anidado en un momento.

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

Aquí el error es bastante autoexplicativo. 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)

Como no hay más variables que *a, *a sorbe todos los valores en la secuencia rvalue. ¿Qué pasa si reemplaza el (1, 2) con un solo valor?

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

se convierte

(*a,) = 1

Nuevamente, el error aquí es autoexplicativo. No puedes descomprimir algo que no sea una secuencia, y *a necesita algo para descomprimir. 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 - necesitas 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"; ¿quizás 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 (2 y 3) están asignados a 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 expliqué anteriormente por qué la primera línea arroja un error. La segunda línea es tonta, pero he aquí por qué funciona:

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

Como se explicó anteriormente, trabajamos desde los extremos. 3 está asignado a c, y luego los valores restantes se asignan a la variable con el * precediéndolo, en este caso, (a, b). Entonces eso es equivalente a (a, b) = (1, 2), que funciona porque hay la cantidad correcta de elementos. No puedo pensar en ninguna razón para que esto aparezca en el código de trabajo. Similar,

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

se convierte

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

Trabajando desde los extremos, 's' está asignado a cy ('t', 'h', 'i') está asignado a (a, *b). Trabajando nuevamente desde los extremos, 't' está asignado a 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.


87
2017-08-06 17:30



Encuentro que la tupla Python 2 es bastante sencilla de desempacar. Cada nombre de la izquierda corresponde con una secuencia completa o un solo elemento en una secuencia a la derecha. Si los nombres corresponden a elementos individuales de cualquier secuencia, entonces debe haber suficientes nombres para cubrir todos los elementos.

El desempaquetado extendido, sin embargo, ciertamente puede ser 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 son así de estructurados, deberían estar en una dict o una instancia de clase, formas no estructuradas como listas.

Claramente, la nueva sintaxis puede ser abusada. La respuesta a tu pregunta es que tu no debería tiene que leer expresiones como esa; son malas prácticas y dudo que sean usadas.

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


6
2017-08-06 15:13



Creo que su código puede ser engañoso usar otro formulario para expresarlo.

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

Prefiero usar el desempaquetado solo para tareas simples como el intercambio.


2
2017-08-06 15:08