Tracks
/
Python
Python
/
Temario
/
Desempaquetado y asignación múltiple
De

Desempaquetado y asignación múltiple en Python

3 ejercicios

Acerca de Desempaquetado y asignación múltiple

El desempaquetado se refiere al acto de extraer los elementos de una colección, como una list, tuple o dict, mediante la iteración. Los valores desempaquetados se pueden asignar a continuación a variables dentro de la misma instrucción. Un ejemplo muy habitual de este comportamiento es for item in list, donde item toma el valor de cada elemento de list por turnos a lo largo de la iteración.

La asignación múltiple es la capacidad de asignar varias variables a valores desempaquetados dentro de una misma instrucción. Esto permite que el código sea más conciso y legible, y se consigue separando con una coma las variables que se van a asignar, como en first, second, third = (1,2,3) o for index, item in enumerate(iterable).

Los operadores especiales * y ** se usan a menudo en contextos de desempaquetado. * se puede usar para combinar varios lists/tuples en un único list/tuple desempaquetando cada uno en un nuevo list/tuple común. ** se puede usar para combinar varios diccionarios en uno solo desempaquetando cada uno en un nuevo dict común.

Cuando el operador * se usa sin una colección, empaqueta una serie de valores en un list. Esto se usa a menudo en la asignación múltiple para agrupar en una sola variable todos los elementos «sobrantes» que no tienen una asignación individual.

En Python también es habitual aprovechar este comportamiento de desempaquetado y empaquetado al usar o definir funciones que aceptan un número arbitrario de argumentos posicionales o de palabra clave. A menudo verás estos parámetros «especiales» definidos como def some_function(*args, **kwargs) y estos argumentos «especiales» usados como some_function(*some_tuple, **some_dict).

Caution

*<variable_name> y **<variable_name> no se deben confundir con * y **. Mientras que * y ** se usan para la multiplicación y la exponenciación respectivamente, *<variable_name> y **<variable_name> se usan como operadores de empaquetado y desempaquetado.

Asignación múltiple

En la asignación múltiple, el número de variables a la izquierda del operador de asignación (=) debe coincidir con el número de valores a la derecha. Para separar los valores, usa una coma ,:

>>> a, b = 1, 2
>>> a
1

Si en la asignación múltiple el número de variables no se corresponde con los valores dados, se lanzará un ValueError:

>>> x, y, z = 1, 2

ValueError: too many values to unpack (expected 3, got 2)

La asignación múltiple no se limita a un solo tipo de datos:

>>> x, y, z = 1, "Hello", True
>>> x
1

>>> y
'Hello'

>>> z
True

La asignación múltiple se puede usar para intercambiar elementos en lists. Esta práctica es bastante habitual en los algoritmos de ordenación. Por ejemplo:

>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]

Como los tuples son inmutables, no puedes intercambiar elementos en un tuple.

Desempaquetado

Note

Los ejemplos siguientes usan lists, pero los mismos conceptos se aplican a los tuples.

En Python, es posible desempaquetar los elementos de un list/tuple/dict en variables distintas. Como los valores aparecen dentro de lists/tuples en un orden concreto, se desempaquetan en variables en ese mismo orden:

>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"

Si hay valores que no necesitas, puedes usar _ para marcarlos:

>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"

Desempaquetado profundo

Desempaquetar y asignar valores de un list/tuple que está dentro de un list o un tuple (también conocidos como arrays/tuplas anidados) funciona igual que un desempaquetado superficial, pero a menudo necesita cualificadores para aclarar el contexto o la posición del valor:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"

>>> d
"potato"

También puedes desempaquetar en profundidad solo una parte de un list/tuple anidado:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]

>>> c
"carrot"

Si el desempaquetado tiene variables en una posición incorrecta o un número incorrecto de valores, obtendrás un ValueError:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [d]] = fruits_vegetables

ValueError: too many values to unpack (expected 1)

Desempaquetar un array/tupla con *

Al desempaquetar un list/tuple puedes usar el operador * para capturar los valores «sobrantes». Esto es más claro que cortar el list/tuple (lo que en algunas situaciones es menos legible). Por ejemplo, podemos extraer el primer elemento y empaquetar los valores restantes en un nuevo list sin el primer elemento:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *last = fruits
>>> x
"apple"

>>> last
["banana", "cherry", "orange", "kiwi", "melon", "mango"]

También podemos extraer los valores del principio y del final del list mientras agrupamos todos los valores intermedios:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *middle, y, z = fruits
>>> y
"melon"

>>> middle
["banana", "cherry", "orange", "kiwi"]

También podemos usar * en el desempaquetado profundo:

>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>>> [[a, *rest], b] = fruits_vegetables
>>> a
"apple"

>>> rest
["banana", "melon"]

Desempaquetar un diccionario

Desempaquetar un diccionario es un poco distinto de desempaquetar un list/tuple. La iteración sobre diccionarios usa por defecto las claves. Así que, al desempaquetar un dict, solo puedes desempaquetar las claves, no los valores:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"

Si quieres desempaquetar los valores, puedes usar el método <dict>.values():

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6

Si necesitas tanto las claves como los valores, usa el método <dict>.items(). <dict>.items() genera una vista iterable que contiene pares clave-valor. Estos se pueden desempaquetar en un tuple:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)

Empaquetado

El empaquetado es la capacidad de agrupar varios valores en un solo list que se asigna a una variable. Esto es útil cuando quieres desempaquetar valores, hacer cambios y luego empaquetar de nuevo los resultados en una variable. También hace posible combinar 2 o más lists/tuples/dicts.

Empaquetar un array/tupla con *

Un list/tuple se puede empaquetar con el operador *. Esto empaquetará todos los valores en un list/tuple.

>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]

# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>>> combined_fruits = *fruits, *more_fruits

# If there is no * on to the left of the "=" the result is a tuple
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")

# If the * operator is used on the left side of "=" the result is a list.
# Note the trailing comma. 
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

# A list literal can be used instead, but might not be as readable.
>>> [*combined_fruits_too] = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

Para más detalles sobre el uso de * y **, consulta PEP 3132 y PEP 448.

Empaquetar un diccionario con **

Un diccionario se empaqueta usando el operador **. Esto empaquetará todos los pares clave-valor de un diccionario en otro, o combinará dos diccionarios entre sí.

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}

# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

Uso de * y ** con funciones

Empaquetar con parámetros de función

Cuando creas una función que acepta un número arbitrario de argumentos, puedes usar *args o **kwargs en la definición de la función. *args se usa para empaquetar un número arbitrario de argumentos posicionales (no de palabra clave) y **kwargs se usa para empaquetar un número arbitrario de argumentos de palabra clave.

Uso de *args:

# This function is defined to take any number of positional arguments

>>> def my_function(*args):
...   print(args)

# Arguments given to the function are packed into a tuple

>>> my_function(1, 2, 3)
(1, 2, 3)

>>> my_function("Hello")
("Hello")

>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")

Uso de **kwargs:

# This function is defined to take any number of keyword arguments

>>> def my_function(**kwargs):
...   print(kwargs)

# Arguments given to the function are packed into a dictionary

>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}

*args y **kwargs también se pueden usar en combinación:

>>> def my_function(*args, **kwargs):
...   print(sum(args))
...   for key, value in kwargs.items():
...       print(str(key) + " = " + str(value))

>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
a = 1
b = 2
c = 3

También puedes escribir parámetros antes de *args para permitir argumentos posicionales concretos. Los argumentos de palabra clave individuales tienen que aparecer entonces antes de **kwargs.

Caution

Los argumentos tienen que estructurarse así:

def my_function(<positional_args>, *args, <key-word_args>, **kwargs)

Si no sigues este orden, obtendrás un error.

>>> def my_function(a, b, *args):
...   print(a)
...   print(b)
...   print(args)

>>> my_function(1, 2, 3, 4, 5)
1
2
(3, 4, 5)

Escribir los argumentos en un orden incorrecto dará lugar a un error:

>>> def my_function(*args, a, b):
...   print(args)

>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
  File "c:\something.py", line 3, in <module>
    my_function(1, 2, 3, 4, 5)
TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'

Desempaquetar en llamadas a funciones

Puedes usar * para desempaquetar un list/tuple de argumentos en una llamada a una función. Esto es muy útil para funciones que no aceptan un iterable:

>>> def my_function(a, b, c):
...   print(c)
...   print(b)
...   print(a)

numbers = [1, 2, 3]
>>> my_function(*numbers)
3
2
1

Usar el desempaquetado con * junto con la función integrada zip() es otro caso de uso habitual. La función zip() toma varios iterables y devuelve un list de tuples con los valores de cada iterable agrupados:

>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]
Editar en GitHub El enlace se abre en una nueva ventana o pestaña

Aprende Desempaquetado y asignación múltiple

La práctica está bloqueada

Desbloquea 3 ejercicios más para practicar Desempaquetado y asignación múltiple