En Python, el operador *
no se limita a multiplicaciones o potencias. Este pequeño símbolo es mucho más poderoso y versátil de lo que parece, ofreciendo funcionalidades avanzadas en diversos contextos:
- Argumentos variables: Puedes usar
*args
en funciones para aceptar una cantidad ilimitada de argumentos posicionales. - Desempaquetado de listas y tuplas: El operador
*
permite expandir estructuras iterables, facilitando la combinación o separación de elementos. - Asignación múltiple: Con
*
, es posible capturar múltiples elementos restantes de una lista en una sola variable. - Separación de argumentos posicionales y nombrados: En funciones,
*
puede actuar como separador entre los dos tipos de argumentos, mejorando la legibilidad y control de llamadas complejas.
Este operador es una herramienta fundamental para escribir código más limpio, flexible y potente en Python.
# 1. Usando *args para argumentos variables en una función
def func(*args):
print(args)
func(1, 2, 3) # Salida: (1, 2, 3)
# 2. Desempaquetado de listas o tuplas
a = [1, 2]
b = [3, 4]
print(*a, *b) # Salida: 1 2 3 4
# 3. Desempaquetado con variables y el operador *
ls = [1, 2, 3, 4, 5]
a, b, *others = ls
print(a, b) # Salida: 1 2
print(others) # Salida: [3, 4, 5]
# 4. Separación de argumentos posicionales y con nombre en una función
def func(a, b, *, c, d):
print(a, b, c, d)
# Llamadas de función
func(1, 2, c=3, d=4) # OK
func(1, 2, 3, 4) # Error: no es posible
*args en los parámetros de función
Podemos añadir *args
en nuestros parámetros de función para permitir que nuestra función acepte cualquier número de argumentos posicionales.
def hello(*args):
print(args)
hello() # ()
hello(1) # (1,)
hello(1, 2) # (1, 2)
hello(1, 2, 3, 4) # (1, 2, 3, 4)
^ Observa que nuestra función hello toma *args
: podemos pasar cualquier número de argumentos a hello, y todos ellos quedarán atrapados en una tupla llamada args
.
Ten en cuenta que podemos reemplazar el nombre de variable args por cualquier nombre de variable válido, pero el *
tiene que estar ahí:
def hello(*stuff):
print(stuff)
hello() # ()
hello(1) # (1,)
hello(1, 2) # (1, 2)
hello(1, 2, 3, 4) # (1, 2, 3, 4)
Si combinamos esto con parámetros normales, por ejemplo a & b, los parámetros normales se rellenarán primero, mientras que args tomará el resto de los argumentos sin rellenar.
def hi(a, b, *args):
print(f"{a=} {b=} {args=}")
hi() # error
hi(1) # error
hi(1, 2) # a=1 b=2 args=()
hi(1, 2, 3, 4) # a=1 b=2 args=(3, 4)
^ aquí, si no pasamos nada a a o b, obtenemos errores como los de los dos primeros ejemplos. args captura todos los demás argumentos posicionales distintos de a y b.
**kwargs en parámetros de función
Si usamos **
en lugar de *
, hacemos lo mismo con los argumentos de palabra clave: añadir **kwargs
a nuestra función le permite aceptar cualquier número de argumentos de palabra clave.
Nota: si no está familiarizado, los argumentos de palabra clave se refieren a los argumentos que pasamos usando un formato varname=value
, como los que se muestran a continuación.
def hi(**kwargs):
print(kwargs)
hi() # {}
hi(a=1) # {'a': 1}
hi(a=1, b=2) # {'a': 1, 'b': 2}
hi(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3}
^
todos los argumentos de palabra clave adicionales se recogerán en un diccionario llamado kwargs
.
Una vez más, no tenemos que usar el nombre kwargs
si no queremos, aunque yo uso el nombre kwargs
por defecto debido a la convención. Sin embargo, el **
tiene que estar ahí.
def hi(**things):
print(things)
hi() # {}
hi(a=1) # {'a': 1}
hi(a=1, b=2) # {'a': 1, 'b': 2}
hi(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3}
Del mismo modo, podemos combinarlos con parámetros normales, pero debemos asegurarnos de pasar primero algo a los parámetros normales. kwargs
almacena todos los demás argumentos de palabra clave no utilizados en un diccionario.
def hi(a, b, **kwargs):
print(f"{a=} {b=} {kwargs=}")
hi() # error
hi(a=1) # error
hi(a=1, b=2) # a=1 b=2 kwargs={}
hi(a=1, b=2, c=3) # a=1 b=2 kwargs={'c': 3}
hi(a=1, b=2, c=3, d=4) # a=1 b=2 kwargs={'c': 3, 'd': 4}
^ si no pasamos nada a a o b, obtenemos errores, al igual que en los dos primeros ejemplos
*args en argumentos de función
También podemos usar * fuera de las funciones. Más concretamente, podemos usar *
para desempaquetar nuestro iterable (piense en lista, tupla, etc.) en los argumentos de nuestra función.
Aquí tenemos una función simple hi(a, b, c)
def hi(a, b, c):
print(f"{a=} {b=} {c=}")
hi(1, 2, 3) # a=1 b=2 c=3
mylist = [4, 5, 6]
hi(*mylist) # a=4 b=5 c=6
podemos llamarlo normalmente usando hi(1, 2, 3)
también podemos pasar una lista de longitud 3 (mylist) con un * delante — hi(*mylist) es lo mismo que hi(4, 5, 6)
Dado que hi(a, b, c) requiere que se le pasen exactamente 3 argumentos, tenemos la libertad de pasar 1 argumento posicional junto con *[5, 6], ya que esto hace 3; solo tenemos que asegurarnos de que se pase el número correcto de argumentos.
def hi(a, b, c):
print(f"{a=} {b=} {c=}")
mylist = [4, 5, 6]
hi(*mylist) # a=4 b=5 c=6
mylist = [5, 6]
hi(4, *mylist) # a=4 b=5 c=6
mylist = [4, 5, 6, 7, 8]
hi(*mylist) # error
**kwargs en los argumentos de función
Y si usamos **
en lugar de *
, podemos hacer lo mismo con los diccionarios.
Aquí, hi(**mydict)
es lo mismo que hi(a=4, b=5, c=6)
def hi(a, b, c):
print(f"{a=} {b=} {c=}")
hi(a=4, b=5, c=6) # a=4 b=5 c=6
mydict = {"a": 4, "b": 5, "c": 6}
hi(**mydict) # a=4 b=5 c=6
De manera similar, también podemos combinar argumentos de palabra clave normales con **mydict
siempre que pasemos todos los argumentos de palabra clave requeridos a nuestra función.
def hi(a, b, c):
print(f"{a=} {b=} {c=}")
mydict = {"a": 4, "b": 5, "c": 6}
hi(**mydict) # a=4 b=5 c=6
mydict = {"b": 5, "c": 6}
hi(a=4, **mydict) # a=4 b=5 c=6
Cualquier parámetro después de * debe ser un argumento de palabra clave.
Definamos una función con *
sola
def hello(a, b, *, c, d):
# c and d MUST be keyword arguments
print(a, b, c, d)
hello(1, 2, c=3, d=4) # 1 2 3 4
hello(a=1, b=2, c=3, d=4) # 1 2 3 4
hello(1, 2, 3, 4) # error
^ esta vez, * no se coloca antes de ningún parámetro, lo que significa que cualquier parámetro definido después de * debe ser un argumento de palabra clave.
En este caso, c y d van después de *, por lo que c y d solo pueden ser argumentos de palabra clave: a Python no le importa si pasamos argumentos posicionales o de palabra clave en a y b, pero c y d deben ser argumentos de palabra clave (o de lo contrario se genera un error).
Por el contrario, si reemplazamos * solo por / solo, esto significa que cualquier parámetro de función definido antes de / debe ser un argumento posicional.
def hello(a, b, *, c, d):
# c and d MUST be keyword arguments
print(a, b, c, d)
hello(1, 2, c=3, d=4) # 1 2 3 4
hello(a=1, b=2, c=3, d=4) # 1 2 3 4
hello(1, 2, 3, 4) # error
Podemos combinarlos para imponer restricciones adicionales en nuestros parámetros de función.
def hola(a, b, /, c, d, *, e, f):
# a and b must be positional arguments
# c and d can be either
# e and f must be keyword arguments
print(a, b, c, d, e, f)
*args en el desempaquetado de tuplas
En Python, podemos utilizar el desempaquetado de tuplas para asignar elegantemente múltiples variables a la vez.
person = ["bob", "m", 20]
name, gender, age = person
print(name) # bob
print(gender) # m
print(age) # 20
Podemos combinar el desempaquetado de tuplas con * si tenemos variables sin usar.
Digamos que tenemos muchos más campos en persona, pero solo necesitamos los dos primeros por ahora; podemos usar *others para asignar todo lo demás a la variable others.
person = ["bob", "m", 20, 1.75, 70, "black"]
name, gender, *others = person
print(name) # bob
print(gender) # m
print(others) # [20, 1.75, 70, 'black']
También podemos cambiar la posición de la variable con *: en este ejemplo, solo necesitamos el primer y el último valor, así que ponemos *others en el medio.
person = ["bob", "m", 20, 1.75, 70, "black"]
name, *others, hair = person
print(name) # bob
print(hair) # black
print(others) # ['m', 20, 1.75, 70]
Y en este ejemplo, solo necesitamos los 2 últimos valores, así que podemos poner *otros al principio.
person = ["bob", "m", 20, 1.75, 70, "black"]
*others, weight, hair = person
print(weight) # 70
print(hair) # black
print(others) # ['bob', 'm', 20, 1.75]
Uso de * al combinar iterables
También podemos usar * para combinar iterables como listas, tuplas, conjuntos, etc.
Aquí, combinamos 2 listas a y b desempaquetándolas en otra lista c
a = [1, 2]
b = [3, 4]
c = [-1, 0, *a, *b, 5, 6]
print(c) # [-1, 0, 1, 2, 3, 4, 5, 6]
Nota: esto también funciona para otros iterables como conjuntos y tuplas.
** en la combinación de diccionarios
De manera similar, ** funciona para las asignaciones de clave-valor de la misma manera que * funciona para valores individuales. Podemos usar ** para desempaquetar diccionarios en otros diccionarios para combinarlos.
x = {"a": 1, "b": 2}
y = {"c": 3, "d": 4}
z = {**x, **y}
print(z) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
También podemos combinar esta técnica con pares clave-valor normales al crear nuestro diccionario combinado.
x = {"a": 1, "b": 2}
y = {"c": 3, "d": 4}
z = {**x, **y, "e": 5}
print(z) # {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
Importar todo desde un módulo
También podemos usar desde modulename import * para decirle a Python que queremos importar todo lo que se puede importar desde algún módulo.
from colorama import *
# importing everything from 'colorama' module
Conclusión
¡Espero que hayas aprendido algo nuevo hoy!