Python *args y **kwargs explicados: Funciones flexibles con ejemplos claros
Esas estrellas en firmas de funciones son menos misteriosas de lo que parecen. Aprenda cómo *args y **kwargs recopilan argumentos, cómo las mismas estrellas los descomprimen y cómo diseñar firmas de funciones que sean difíciles de usar incorrectamente.
*argsrecopila argumentos posicionales adicionales en una tupla.**kwargsrecopila argumentos de palabras clave adicionales en un diccionario.- Los nombres son sólo una convención, ya que
*y**son lo que importa. - Lo mismo protagoniza desempaquetar una lista o diccionario cuando llamas a una función.
Tarde o temprano, leerás una función como def handler(*args, **kwargs) y te preguntarás qué acepta. Python *args y **kwargs permiten que una función tome una cantidad flexible de argumentos. Impulsan decoradores, contenedores y muchas API de biblioteca.
Esta guía cubre cómo funcionan en ambas direcciones, junto con las formas más nuevas en que Python le permite controlar cómo se llama una función. Ejecutamos cada ejemplo en Python 3.13 y el resultado que se muestra proviene de esa ejecución.
¿Qué hace *args?
Un parámetro escrito con una estrella recopila cualquier argumento posicional adicional en una tupla:
def total(*args):
print(type(args).__name__, args)
return sum(args)
print(total(1, 2, 3))
# tuple (1, 2, 3)
# 6
print(total())
# tuple ()
# 0
Sin argumentos, simplemente obtienes una tupla vacía, por lo que la función sigue funcionando. El nombre args es una convención. *numbers se comportaría igual y, a menudo, un nombre más descriptivo es mejor.
¿Qué hace **kwargs?
Dos estrellas recopilan los argumentos de palabras clave adicionales en un diccionario:
def describe(**kwargs):
print(type(kwargs).__name__, kwargs)
describe(name="Asha", city="Pune")
# dict {'name': 'Asha', 'city': 'Pune'}
Debido a que es un diccionario normal, puedes recorrerlo, buscar claves con get() y pasarlo. Las claves mantienen el orden en que fueron pasadas.
Orden de parámetros
Cuando mezclas tipos de parámetros, Python requiere un orden específico:
- Parámetros posicionales regulares
*argumentos- Parámetros de solo palabras clave
**kwargs
def order(item, qty=1, *extras, priority=False, **notes):
print(item, qty, extras, priority, notes)
order("pen")
# pen 1 () False {}
order("pen", 2, "gift wrap", "card", priority=True, note="leave at door")
# pen 2 ('gift wrap', 'card') True {'note': 'leave at door'}
Aviso priority. Debido a que viene después de *extras, sólo se puede configurar por nombre, por lo que un argumento posicional perdido nunca podría configurarlo por accidente. No se permite colocar **kwargs antes, y nuestra prueba generó un SyntaxError: ”los argumentos no pueden seguir el argumento de palabra clave var”.
Desempacar al llamar
Las mismas estrellas funcionan a la inversa en el lugar de la llamada. Una estrella distribuye una lista o tupla en argumentos posicionales y dos estrellas distribuyen un diccionario en argumentos de palabras clave:
nums = [4, 5, 6]
total(*nums) # same as total(4, 5, 6) -> 15
settings = {"name": "Ben", "city": "Delhi"}
describe(**settings) # same as describe(name="Ben", city="Delhi")
El desempaquetado funciona con cualquier función, no solo con aquellas que usan *args y **kwargs. Esto resulta útil cuando la configuración llega como un diccionario:
def connect(host, port, timeout=5):
return f"{host}:{port} (timeout {timeout})"
connect(**{"host": "db.local", "port": 5432})
# 'db.local:5432 (timeout 5)'
connect(**{"host": "db.local", "port": 5432, "ssl": True})
# TypeError: connect() got an unexpected keyword argument 'ssl'
Ese error es útil. Una función sin **kwargs rechaza opciones desconocidas, por lo que un error tipográfico en el nombre de una configuración falla ruidosamente en lugar de ignorarse silenciosamente.
Argumentos de solo palabras clave
Un simple * en la firma, sin nombre, significa “todo lo que sigue a esto debe pasarse por palabra clave”:
def make_user(name, *, admin=False):
return name, admin
make_user("Asha", admin=True)
# ('Asha', True)
make_user("Asha", True)
# TypeError: make_user() takes 1 positional argument but 2 were given
Esta es una excelente manera de proteger indicadores booleanos. Una llamada como make_user("Asha", True) obliga a los lectores a adivinar qué significa True. Requerir admin=True hace que cada llamada se explique por sí misma.
Argumentos solo posicionales
Desde Python 3.8, un / marca los parámetros que solo se pueden pasar por posición:
def area(width, height, /):
return width * height
area(3, 4) # 12
area(width=3, height=4)
# TypeError: area() got some positional-only arguments
# passed as keyword arguments: 'width, height'
Esto permite que una biblioteca cambie el nombre de esos parámetros más adelante sin romper el código de nadie, ya que nadie puede depender de los nombres. Varios integrados funcionan de esta manera. Por ejemplo, len(obj=[1]) no funciona.
Consejo: El patrón completo es def f(pos_only, /, normal, *, kw_only). Rara vez necesitará los tres, pero saber qué significan / y * le ayudará a leer la documentación y las firmas de la biblioteca.
Decoradores y envoltorios
Esta es la principal razón en el mundo real para utilizar ambos juntos. Un contenedor a menudo necesita aceptar cualquier argumento que tome la función original y pasarlo sin cambios:
import functools, time
def timed(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
return result
return wrapper
@timed
def slow_add(a, b, delay=0.01):
time.sleep(delay)
return a + b
print(slow_add(2, 3, delay=0.02))
# slow_add took 0.0205s
# 5
El contenedor no necesita conocer los parámetros de slow_add en absoluto, por lo que funciona para cualquier función. Su cifra de tiempo diferirá ligeramente. Nuestra guía para decoradores de Python explica functools.wraps y otros detalles.
Opciones de fusión
** también funciona dentro de un literal de diccionario, lo que proporciona una forma sencilla de aplicar anulaciones además de los valores predeterminados:
defaults = {"theme": "light", "lang": "en"}
def build(**overrides):
return {**defaults, **overrides}
build(theme="dark")
# {'theme': 'dark', 'lang': 'en'}
Las claves posteriores ganan, por lo que la anulación reemplaza la predeterminada. Las subclases también usan este patrón, como en super().__init__(*args, **kwargs), para pasar argumentos adicionales a una clase principal. Consulte clases y objetos de Python para obtener más información sobre super().
hoja de trucos
| Sintaxis | donde | Significado |
|---|---|---|
def f(*argumentos) |
Definición | Reúna argumentos posicionales adicionales en una tupla |
def f(**kwargs) |
Definición | Recopile argumentos de palabras clave adicionales en un dictado |
definición f(a, *, b) |
Definición | b es solo para palabras clave |
definición f(a, /, b) |
Definición | a es solo posicional |
f(*items) |
llamar | Distribuir una secuencia en argumentos posicionales. |
f(**options) |
llamar | Difunda un dictado en argumentos de palabras clave |
{**a, **b} |
literales | Fusionar diccionarios |
[*a, *b] |
literales | Combina iterables en una lista |
Cuando evitarlos
La flexibilidad tiene un precio. Una función definida como def create(**kwargs) no dice nada a los lectores sobre qué opciones existen y los editores no pueden autocompletarlas. Los errores tipográficos pueden pasar silenciosamente a través de varias capas antes de que algo falle.
Por lo tanto, prefiera parámetros explícitos cuando sepa lo que necesita una función. Utilice *args y **kwargs cuando realmente necesite flexibilidad: envoltorios, decoradores, reenvío de argumentos a otra función o una entrada verdadera de longitud variable como total(*numbers). Si una función necesita muchas opciones con nombre, un dataclass también puede ser más claro.
Errores comunes
Usando un valor predeterminado mutable
No se trata de estrellas, pero suele aparecer cerca de ellas. Un valor predeterminado se crea una vez, cuando se define la función, y luego se comparte entre llamadas:
def bad(items=[]):
items.append(1)
return items
print(bad(), bad())
# [1, 1] [1, 1]
Ambas llamadas arrojaron la misma lista, que para entonces contenía dos elementos. Utilice items=None y cree una nueva lista dentro de la función.
Otros errores
- Olvidarse de desempacar. Llamar a
total(nums)pasa una lista en lugar de tres números. Escribetotal(*nums). - Tragar errores tipográficos. Una función que acepta
**kwargspero ignora claves desconocidas oculta opciones mal escritas. Valídalos o evítalos**kwargs. - Pasar el mismo argumento dos veces.
f(1, **{"a": 2}), dondeaes el primer parámetro, genera un error de “valores múltiples”. - No reenviar en un contenedor. Llamar a
func(args, kwargs)pasa una tupla y un dict. La forma correcta esfunc(*args, **kwargs).
Escriba sugerencias para *args y **kwargs
Las sugerencias de tipo en *args y **kwargs describen cada valor individual, no la colección completa. Esto sorprende a mucha gente:
def total(*prices: float) -> float:
return sum(prices) # prices is tuple[float, ...]
def tag(**attrs: str) -> str:
return " ".join(f'{k}="{v}"' for k, v in attrs.items()) # dict[str, str]
total(1.5, 2.5) # 4.0
tag(id="x", role="button") # id="x" role="button"
Aquí prices se trata como una tupla de flotantes y attrs como un diccionario de cadenas. Cuando los argumentos de palabras clave tienen diferentes tipos, Python 3.12 agregó una opción precisa: un TypedDict combinado con Unpack.
from typing import TypedDict, Unpack
class Options(TypedDict, total=False):
timeout: float
retries: int
def fetch(url: str, **opts: Unpack[Options]) -> None: ...
Un verificador de tipos como mypy o Pyright puede entonces rechazar fetch(url, retries="3") o un nombre de opción mal escrito, lo que un simple **kwargs nunca puede hacer. Sin embargo, el comportamiento en tiempo de ejecución no cambia. Cuando ejecutamos fetch("u", retries="3") en Python 3.13, se ejecutó sin quejas, porque Python en sí no aplica sugerencias de tipo.
Si se encuentra documentando una larga lista de claves aceptadas para **kwargs, considere reemplazarlas con parámetros explícitos de solo palabras clave. Luego, los editores pueden autocompletarlos y los errores se detectan de inmediato.
Conclusión
Recuerda las dos direcciones. En una definición, las estrellas se acumulan. En una llamada, desempacan. Agregue argumentos de solo palabras clave para que las llamadas sean más claras y reserve **kwargs para el código que realmente reenvía argumentos. Para practicar, escriba un pequeño decorador que registre los argumentos de cada llamada y utilícelo en algunas funciones diferentes.
Preguntas frecuentes
¿Cuál es la diferencia entre *args y **kwargs?
*args recopila argumentos posicionales adicionales en una tupla. **kwargs recopila argumentos de palabras clave adicionales en un diccionario. Una función puede utilizar ambos juntos.
¿Tengo que llamarlos args y kwargs?
No. Las estrellas hacen el trabajo, por lo que *valores u **opciones son igualmente válidos. args y kwargs son simplemente una convención ampliamente reconocida.
¿En qué orden deben ir *args y **kwargs?
Primero los parámetros regulares, luego *args, luego los parámetros de solo palabras clave y **kwargs por último. Cualquier otro orden provoca un error de sintaxis.
¿Qué significa un solo * en la definición de una función?
Un * simple significa que cada parámetro posterior debe pasarse mediante palabra clave. Hace que las llamadas sean más claras, especialmente para indicadores booleanos.
¿Qué significa / en una función de Python?
Los parámetros antes de / son solo posicionales, por lo que quienes llaman no pueden pasarlos por nombre. Esta característica se agregó en Python 3.8.
sigue aprendiendo
¿Necesitas ayuda con pruebas, WordPress o SEO?
Ayudamos a los equipos a enviar mejor software y hacer crecer sus sitios. Cuéntenos en qué está trabajando y le sugeriremos un siguiente paso claro.
Más guías sobre testing, IA y desarrollo moderno
¿Te gustó este artículo? Elige un tema y salta directo a nuestros tutoriales más recientes.
Explicación de las alucinaciones de la IA: por qué los modelos de lenguaje inventan cosas y cómo reducirlas
Se explicaron las alucinaciones de la IA: qué son, por qué los modelos de lenguaje las producen, tipos comunes, cómo reducirlas con conexión a tierr…
Leer artículo →DevOpsExplicación de las variables de entorno: archivos .env, secretos y configuración bien hechos
Variables de entorno explicadas con ejemplos de Python que ejecutamos: leerlas y configurarlas, por qué cada valor es una cadena, archivos .env, her…
Leer artículo →Desarrollo de softwareExplicación de la limitación de velocidad de API: algoritmos, encabezados y manejo de errores 429
La limitación de la tasa de API se explica con una simulación que ejecutamos: depósito de tokens, ventana fija, ventana deslizante, 429 respuestas, …
Leer artículo →Automatización de pruebasBurlarse en Python: unittest.mock, patch y los errores que hacen que las pruebas mientan
Burlarse en Python con unittest.mock, probado: Mock y return_value, side_effect, parches donde se usa un nombre, especificación y autospec, fechas d…
Leer artículo →Pruebas de softwareExplicación de la cobertura del código: cobertura de línea versus sucursal y lo que realmente significa 100%
Cobertura del código explicada con una ejecución real de cobertura.py: cobertura de línea, rama y condición, lectura de un informe, por qué una cobe…
Leer artículo →Bases de datos y SQLDELETE vs TRUNCATE vs DROP en SQL: diferencias, reversión y cuándo usar cada uno
DELETE vs TRUNCATE vs DROP explicado con SQL probado: qué elimina cada uno, reversión, activadores, restablecimiento de identidad, velocidad y en qu…
Leer artículo →Bases de datos y SQLVistas SQL explicadas: cree, use y evite los errores, con ejemplos
Vistas SQL explicadas con consultas que ejecutamos: creación de una vista, resultados en vivo, ocultación de columnas, rendimiento, vistas actualiza…
Leer artículo →Desarrollo de softwareInterfaz TypeScript versus tipo: diferencias reales, probadas con el compilador
Interfaz TypeScript versus tipo, verificado con el compilador real: qué pueden hacer ambos, qué puede hacer solo uno, fusión de declaraciones, exten…
Leer artículo →Desarrollo de softwarePython *args y **kwargs explicados: funciones flexibles con ejemplos claros
Python *args y **kwargs explicados con el código que ejecutamos: empaquetado y desempaquetado, orden de parámetros, argumentos solo de palabras clav…
Leer artículo →Desarrollo de softwareFunciones Lambda de Python: sintaxis, usos reales y cuándo no utilizarlas
Funciones lambda de Python explicadas con el código que ejecutamos: sintaxis, clasificación con clave, mapa y filtro, cierres, trampa de enlace tard…
Leer artículo →Inteligencia artificial¿Qué son los agentes de IA? Cómo funcionan, con un bucle de agente simple que puedes ejecutar
¿Qué son los agentes de IA? Una guía sencilla sobre el bucle del agente, las herramientas, la memoria y las barreras de seguridad, con un pequeño bu…
Leer artículo →DevOpsMonitoreo versus observabilidad: la verdadera diferencia, con registros, métricas y seguimientos
Explicación del monitoreo versus observabilidad: fallas conocidas y desconocidas, registros, métricas y seguimientos, un recorrido por incidentes de…
Leer artículo →DevOpsDocker Volumes versus Bind Mounts: persistir los datos del contenedor de la manera correcta
Se explican los volúmenes de Docker frente a los montajes de enlace: por qué desaparecen los datos del contenedor, volúmenes con nombre, montajes de…
Leer artículo →Automatización de pruebasTDD vs BDD: diferencias clave, una función creada en ambos sentidos y cuándo usar cada una
TDD vs BDD explicado con una característica de descuento construida en ambos sentidos: refactor rojo-verde con el código que ejecutamos, escenarios …
Leer artículo →Desarrollo de softwareGit merge vs rebase: diferencias, ejemplos y cuándo usar cada uno
Git merge vs rebase explicado con salida real del log, una tabla comparativa, la regla de oro del rebase y un flujo de trabajo seguro para equipos.
Leer artículo →Desarrollo de softwareList comprehension en Python: sintaxis, ejemplos y cuándo evitarla
List comprehension en Python explicada: sintaxis, filtros, if-else, bucles anidados, tiempos medidos y cuándo es mejor un bucle for normal.
Leer artículo →