python python-3.x iterable-unpacking argument-unpacking

python - Desembalaje, desempaquetado extendido y desembalaje extendido anidado



python-3.x iterable-unpacking (3)

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.

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?


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 instancia dict o class, no en formas no estructuradas como listas.

Claramente, la nueva sintaxis puede ser abusada. La respuesta a su pregunta es que no debería tener que leer expresiones como esa; son malas prácticas y dudo que se usen.

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 no es así .


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.

A los fines de desempaquetar solamente, las siguientes sustituciones son válidas en el lado derecho de = (es decir, para valores r ):

''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, para lvalues y rvalues ):

''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 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 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 a la derecha se desempaqueta en (c,) . Pero las secuencias tienen una longitud diferente, por lo que hay un error.

Ahora para un desembalaje extendido usando el operador * . Esto es un poco más complejo, pero aún es bastante sencillo. Una variable precedida por * 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 .

Los valores L como (*a, b) y (a, *b) son solo casos especiales de los anteriores. 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 b 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 otras variables que no sean *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 - se 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"; ¿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 ) se asignan 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 se asigna a c , y luego los valores restantes se asignan a la variable con el * precedente, 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'' se asigna a c , y (''t'', ''h'', ''i'') se asigna a (a, *b) . Al trabajar nuevamente desde los extremos, ''t'' se asigna a a , y (''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.