El desempaquetado se refiere al acto de extraer los elementos de una colección, como una list, una tuple o un dict, usando iteración.
Los valores desempaquetados se pueden asignar entonces a variables dentro de la misma sentencia.
Un ejemplo muy común de este comportamiento es for item in list, donde item toma el valor de cada elemento de la list a su vez 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 sentencia.
Esto permite que el código sea más conciso y legible, y se hace 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 varias list/tuple en una sola list/tuple al desempaquetar cada una en una nueva list/tuple común.
** se puede usar para combinar varios diccionarios en uno solo al desempaquetar cada uno en un nuevo dict común.
Cuando el operador * se usa sin una colección, empaqueta una serie de valores en una 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 común aprovechar este comportamiento de desempaquetado/empaquetado al usar o definir funciones que toman una cantidad arbitraria 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).
*<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.
En la asignación múltiple, la cantidad de variables del lado izquierdo del operador de asignación (=) debe coincidir con la cantidad de valores del lado derecho.
Para separar los valores, usa una coma ,:
>>> a, b = 1, 2
>>> a
1
Si la asignación múltiple recibe una cantidad incorrecta de variables para 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 común en los algoritmos de ordenamiento.
Por ejemplo:
>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]
Como las tuples son inmutables, no puedes intercambiar elementos en una tuple.
Los ejemplos a continuación usan lists, pero los mismos conceptos se aplican a las tuples.
En Python, es posible desempaquetar los elementos de una list/tuple/dict en variables distintas.
Como los valores aparecen dentro de las lists/tuples en un orden específico, 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"
Desempaquetar y asignar valores de una list/tuple que está dentro de otra list o tuple (también conocido como arrays/tuplas anidados) funciona igual que un desempaquetado superficial, pero a menudo necesita calificadores 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 profundamente solo una parte de una list/tuple anidada:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]
>>> c
"carrot"
Si en el desempaquetado hay variables con una ubicación incorrecta o una cantidad incorrecta 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)
*
Al desempaquetar una list/tuple puedes usar el operador * para capturar los valores «sobrantes».
Esto es más claro que rebanar la list/tuple (lo que en algunas situaciones es menos legible).
Por ejemplo, podemos extraer el primer elemento y empaquetar los valores restantes en una nueva 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 el final de la list mientras agrupamos todos los valores del medio:
>>> 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"]
El desempaquetado de un diccionario es un poco diferente del desempaquetado de una list/tuple.
La iteración sobre diccionarios usa las claves por defecto.
Entonces, al desempaquetar un dict, solo puedes desempaquetar las claves y 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 una tuple:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)
El empaquetado es la capacidad de agrupar varios valores en una sola list que se asigna a una variable.
Esto es útil cuando quieres desempaquetar valores, hacer cambios y luego empaquetar los resultados de vuelta en una variable.
También hace posible combinar 2 o más lists/tuples/dicts.
*
Empaquetar una list/tuple se puede hacer con el operador *.
Esto empaquetará todos los valores en una 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 la PEP 3132 y la PEP 448.
**
Empaquetar un diccionario se hace usando el operador **.
Esto empaquetará todos los pares clave-valor de un diccionario en otro diccionario, 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}
* y ** con funcionesCuando creas una función que acepta una cantidad arbitraria de argumentos, puedes usar *args o **kwargs en la definición de la función.
*args se usa para empaquetar una cantidad arbitraria de argumentos posicionales (no de palabra clave) y
**kwargs se usa para empaquetar una cantidad arbitraria 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 entre sí:
>>> 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 específicos.
Entonces, los argumentos de palabra clave individuales tienen que aparecer antes de **kwargs.
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á como resultado 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'
Puedes usar * para desempaquetar una 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 común.
La función zip() toma varios iterables y devuelve una 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)]