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
*argsen 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!