DMarlon Studio

Lección 2 · 2 de 20

*args, **kwargs y argumentos por nombre · Python

Funciones de Python que aceptan cualquier número de argumentos con *args y **kwargs, y argumentos que solo se pasan por nombre.

Python · *args, **kwargs y argumentos por nombre en DMarlon Studio: el código del ejemplo y su salida en la terminal

invoice_total(*amounts, ...) acepta tantos importes como se le pasen: el asterisco los reúne en una tupla llamada amounts, y sum(amounts) los suma. Por eso funciona igual con tres importes que con uno.

Los parámetros que van detrás de *amounts (discount y tax) son solo por nombre (*keyword-only*): no hay manera de pasarlos por posición, porque todo lo posicional ya se lo queda *amounts. Hay que escribir discount=0.1 o tax=0.10, y eso hace la llamada más clara.

log_event(event, **details) recibe los argumentos con nombre que sobran en un diccionario. El programa lo recorre con details.items() para escribir cada campo como clave=valor.

Los asteriscos también sirven al llamar: invoice_total(*prices) reparte la lista como argumentos sueltos y log_event("update", **extra) reparte un diccionario como argumentos con nombre.

En ship(order_id, *, express=False) el * solo no recoge nada: marca que lo que viene detrás va por nombre. Por eso ship(1043, True) lanza un TypeError, que el programa captura y muestra.

Código · main.py

def invoice_total(*amounts, discount=0.0, tax=0.21):    """Any number of amounts; discount and tax are keyword-only."""    net = sum(amounts) * (1 - discount)    return round(net * (1 + tax), 2)def log_event(event, **details):    """Extra named arguments arrive in a dict."""    fields = ", ".join(f"{key}={value}" for key, value in details.items())    print(f"[{event}] {fields}")def ship(order_id, *, express=False):    """The bare * makes express keyword-only."""    speed = "express" if express else "standard"    print(f"Order {order_id} ships by {speed} delivery")print("Total:", invoice_total(100, 50, 25))print("With 10% off:", invoice_total(100, 50, 25, discount=0.1))print("Reduced tax:", invoice_total(80, tax=0.10))prices = [19.99, 5.01, 75.0]print("From a list:", invoice_total(*prices))  # unpack a list into *amountslog_event("login", user="ana", ip="10.0.0.7")extra = {"order": 1042, "status": "shipped"}log_event("update", **extra)       # unpack a dict into **detailsship(1042, express=True)try:    ship(1043, True)               # positional: not allowedexcept TypeError as error:    print("Error:", error)

Salida · python3 main.py

Total: 211.75
With 10% off: 190.57
Reduced tax: 88.0
From a list: 121.0
[login] user=ana, ip=10.0.0.7
[update] order=1042, status=shipped
Order 1042 ships by express delivery
Error: ship() takes 1 positional argument but 2 were given

Para recordar

  • *args reúne los argumentos posicionales sobrantes en una tupla.
  • **kwargs reúne los argumentos con nombre sobrantes en un diccionario.
  • Lo que va detrás de * o de *args solo se pasa por nombre.
  • Al llamar, *lista y **dic reparten los valores.
  • Los nombres args y kwargs son costumbre; lo que importa son los asteriscos.

Ejercicio

Añada a invoice_total un parámetro solo por nombre shipping=0.0 que se sume al final, y llámelo con shipping=4.95.