Historia de la pregunta:
Los operadores * y ** para desempacar aparecieron en Python hace mucho tiempo, pero su uso se ha ampliado con cada versión (por ejemplo, desde Python 3.5 se añadió soporte para combinar varias colecciones a través de * y **). Estos operadores hacen que trabajar con colecciones sea más flexible, incluso al pasar argumentos y recopilarlos en funciones.
Problema:
Sin desempaquetado, al trabajar con secuencias dinámicas, al pasar parámetros con un número variable de argumentos, surge la necesidad de escribir ciclos manualmente y verificar la dimensionalidad de las colecciones. Los errores pueden aparecer fácilmente con un uso incorrecto o si no se distingue la función de * y **.
Solución:
El operador * está destinado a desempaquetar secuencias (lista, tuple, conjunto), mientras que el operador ** se usa para desempacar diccionarios al llamar funciones o al combinar varios diccionarios. Permiten pasar argumentos de forma elegante, fusionar colecciones y convertir fácilmente estructuras arbitrarias en parámetros de función.
Ejemplo de código:
def foo(a, b, c): print(a, b, c) args = (1, 2, 3) foo(*args) # 1 2 3 params = {'a': 10, 'b': 20, 'c': 30} foo(**params) # 10 20 30 list1 = [1, 2] list2 = [3, 4] combined = [*list1, *list2] print(combined) # [1, 2, 3, 4]
Características clave:
*sequence pasa elementos como argumentos posicionales separados, **dict como argumentos nombrados.[*a, *b] y {**d1, **d2}.¿Se pueden usar * y ** para colecciones mixtas?
Al llamar a una función, * solo funciona con argumentos posicionales, y ** solo con argumentos nombrados. Si se pasa un diccionario no desempacado como * o una secuencia como **, se producirá un error.
def foo(a, b): print(a, b) foo(*{'a': 1, 'b': 2}) # Imprime: a b (¡claves del diccionario, no valores!)
**¿Qué pasa si los nombres de las claves se superponen al combinar diccionarios a través de {**d1, d2}?
El resultado será el valor del último diccionario con esa clave.
d1 = {'x': 1, 'y': 2} d2 = {'y': 33, 'z': 44} merged = {**d1, **d2} print(merged) # {'x': 1, 'y': 33, 'z': 44}
¿Se pueden usar * y ** dentro de comprensiones de listas o diccionarios?
Sí, esto es legal desde Python 3.5, por ejemplo:
lst = [1, 2, *range(3, 6)] # [1, 2, 3, 4, 5] dct = {**{'a': 1}, 'b': 2, **{'c': 3}}
Un desarrollador acepta un diccionario en una función y lo descompone con * en lugar de **. Ocurre un comportamiento inesperado: en la función entran las claves, no los valores.
Ventajas:
El código no falla de inmediato, parece “funcionar”.
Desventajas:
Errores ocultos y discrepancia con la lógica esperada.
Paso correcto de parámetros a través de **kwargs, fusión cuidadosa de diccionarios, uso de * en la combinación dinámica de secuencias.
Ventajas:
Máxima flexibilidad, concisión del código, facilidad de refactorización.
Desventajas:
Con un gran número de parámetros y colecciones, es importante prestar atención a los nombres y el orden, de lo contrario pueden surgir errores.