Pregunta 1¿Qué imprime este código?def f(*args):
return sum(args)
print(f(1, 2, 3, 4))
Argumentos de longitud variable *args / **kwargs y múltiples valores de retorno
Aprende a recibir cualquier número de argumentos con args y *kwargs y a devolver varios valores como tupla.
En el artículo anterior sobre def, definiste funciones con un número fijo de argumentos. En el código real, sin embargo, el número de argumentos no siempre se conoce de antemano, y a veces quieres devolver más de un resultado en una sola llamada.
Python maneja esto con argumentos de longitud variable y múltiples valores de retorno. Ambos se basan directamente en lo que ya sabes sobre tuplas y dicts.
Argumentos posicionales de longitud variable *args — recolectar en una tupla
Pon un solo asterisco delante del nombre de un argumento (*args) y la función puede aceptar cualquier número de argumentos posicionales. Dentro de la función, esos argumentos están disponibles como una tupla.
El nombre args es la convención, pero técnicamente cualquier nombre descriptivo funciona — *prices, *messages, lo que se lea bien para el caso de uso.
# Acepta cualquier número de argumentos posicionales
def sum_all(*prices):
print(prices) # (100, 200, 300)
print(type(prices)) # <class 'tuple'>
return sum(prices)
print(sum_all(100, 200, 300)) # 600
print(sum_all(500)) # 500 (uno también vale)
print(sum_all()) # 0 (cero da una tupla vacía)
Combinando argumentos normales con *args
Coloca argumentos normales antes de *args y podrás mezclar un argumento inicial fijo con una cola de longitud variable. Escribir log(level, *messages) significa que el primer valor se asigna a level y todo lo demás se recolecta en una tupla llamada messages. El orden es argumentos normales → *args.
def log(level, *messages):
for m in messages:
print(f"[{level}] {m}")
log("INFO", "Iniciado", "Conectado")
# [INFO] Iniciado
# [INFO] Conectado
Argumentos por palabra clave de longitud variable **kwargs — recolectar en un dict
Dos asteriscos (`kwargs) permiten que la función acepte cualquier argumento por palabra clave en forma de name=value. Dentro de la función, esos argumentos llegan como un dict, lo que hace que esto sea perfecto cuando las claves no se conocen de antemano**.
El nombre convencional es kwargs (abreviatura de keyword arguments).
Llamar a show(name="Ana", age=30) le da al receptor el dict {"name": "Ana", "age": 30}. El hecho de no tener que declarar las claves de antemano es la idea principal.
def describe_user(**info):
print(info) # {'name': 'Ana', 'age': 30, 'city': 'Madrid'}
print(type(info)) # <class 'dict'>
for key, value in info.items():
print(f"{key}: {value}")
describe_user(name="Ana", age=30, city="Madrid")
Combinando argumentos normales con **kwargs
Pon un argumento normal antes de **kwargs y obtendrás **un valor obligatorio fijo** más **campos opcionales flexibles**. Definir def save_user(name, **info): significa que name siempre es obligatorio, mientras que campos extra como age=30 o city="Madrid" se recolectan en el dict info.
def save_user(name, **info):
print(f"name: {name}")
for key, value in info.items():
print(f"{key}: {value}")
save_user("Ana", age=30, city="Madrid")
# name: Ana
# age: 30
# city: Madrid
Usando argumentos normales, *args y **kwargs juntos
Los tres tipos de argumentos pueden coexistir en una sola función. El orden es fijo: argumentos normales → *args → **kwargs. Escribir def register(user_id, *tags, **meta): asigna el primer valor a user_id, recolecta cualquier otro argumento posicional en la tupla tags, y recolecta cada argumento key=value en el dict meta.
def register(user_id, *tags, **meta):
print(f"id: {user_id}")
print(f"tags: {tags}")
print(f"meta: {meta}")
register(1001, "admin", "beta", email="ana@example.com", active=True)
# id: 1001
# tags: ('admin', 'beta')
# meta: {'email': 'ana@example.com', 'active': True}
Desempaquetado del lado de la llamada — *list / **dict
Cuando pasas una lista o dict existente a una función, el hecho de poner o no * o ** delante cambia el resultado drásticamente.
Si la pasas tal cual sin * o **, la lista o dict se trata **como un solo valor que no se desempaqueta**. Recíbela a través de *args y acabarás con una tupla que contiene una lista (p. ej. ([1, 2, 3],)`) — esa es la trampa.
Con `list / dict del lado de la llamada, el contenido se expande uno por uno, así que *args / **kwargs ven la tupla (1, 2, 3) o el dict {"name": "Ana"} esperados.
def sum_all(*prices):
return sum(prices)
prices = [120, 280, 550]
# X: pasada como una sola lista
# print(sum_all(prices)) # error (sum intenta sumar la lista misma a números)
# O: expándela con `*`
print(sum_all(*prices)) # 950 — igual que sum_all(120, 280, 550)
# La misma idea con dicts
def announce(name, city):
print(f"{name} de {city}")
user = {"name": "Carlos", "city": "Sevilla"}
announce(**user) # igual que announce(name="Carlos", city="Sevilla")
El desempaquetado brilla al pasar datos existentes
Cuando ya tienes los datos como una lista o dict, el desempaquetado los pasa a una función en una sola línea.
Por ejemplo, el dict user = {"name": "Carlos", "city": "Sevilla"} se convierte en announce(**user) — eso es lo mismo que escribir announce(name="Carlos", city="Sevilla"). No tienes que extraer cada clave a mano.
Múltiples valores de retorno — devolver como tupla, desempaquetar al recibir
return acepta múltiples valores separados por comas, y el llamador obtiene una tupla con esos valores. Recíbelos con a, b = funcion() para desempaquetar la tupla en varias variables de una sola vez.
Esto es útil siempre que quieras devolver varios valores desde una función (mínimo y máximo, nombre de usuario y rol, bandera de éxito y mensaje, y así sucesivamente).
# Devuelve varios valores a la vez
def get_person():
return "Ana", 30, "Madrid"
# Recibe como tupla
result = get_person()
print(result) # ('Ana', 30, 'Madrid')
print(type(result)) # <class 'tuple'>
# O desempaqueta en variables individuales
name, age, city = get_person()
print(name, age, city) # Ana 30 Madrid
# Ejemplo práctico: devuelve mín y máx juntos
def get_min_max(values):
return min(values), max(values)
lowest, highest = get_min_max([120, 500, 300, 180])
print(f"min {lowest} / max {highest}")
Usa *rest para capturar el resto
Dada una lista de notas como return 90, 85, 78, 92, 88, escribe first, *middle, last = ... y obtendrás first=90, last=88, middle=[85, 78, 92] — el primer y el último valor, con el resto recolectado en una lista. Es la herramienta adecuada cuando quieres quedarte solo con el principio y el final y agrupar todo lo que hay en medio.
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2¿Cuál es el tipo de kwargs recibido por def f(**kwargs):?
Pregunta 3Dado nums = [1, 2, 3], ¿qué llamada pasa su contenido como tres argumentos posicionales a f(a, b, c)?