Parámetros, defaults, *args y **kwargs: firmas flexibles
Python ofrece el sistema de parámetros más flexible de los lenguajes populares. Con *args y **kwargs puedes escribir funciones que se adaptan a cualquier cantidad de argumentos.
Parámetros posicionales y keyword
Python soporta dos formas de pasar argumentos: posicionales (por orden) y keyword (por nombre):
def crear_perfil(nombre, edad, ciudad):
return f"{nombre}, {edad} años, {ciudad}"
# Argumentos posicionales — el orden importa
print(crear_perfil("Ana", 28, "Madrid"))
# Argumentos keyword — el nombre importa, el orden no
print(crear_perfil(edad=28, ciudad="Madrid", nombre="Ana"))
# Mixto: posicionales primero, keywords después
print(crear_perfil("Ana", ciudad="Madrid", edad=28))
# ❌ Error: posicional después de keyword
# crear_perfil(nombre="Ana", 28, "Madrid") # SyntaxError
# Forzar solo keyword con * en la firma
def configurar(*, host, port, debug=False):
"""Solo acepta keyword arguments."""
return f"{host}:{port} (debug={debug})"
# configurar("localhost", 8000) # TypeError
print(configurar(host="localhost", port=8000))
Ana, 28 años, Madrid
Ana, 28 años, Madrid
Ana, 28 años, Madrid
localhost:8000 (debug=False)Valores por defecto
Los parámetros con valor por defecto son opcionales al llamar la función. Deben ir siempre después de los parámetros sin default:
def saludar(nombre: str, saludo: str = "Hola", puntuacion: str = "!") -> str:
return f"{saludo}, {nombre}{puntuacion}"
print(saludar("Ana")) # Hola, Ana!
print(saludar("Carlos", "Buenos días")) # Buenos días, Carlos!
print(saludar("Beatriz", puntuacion="...")) # Hola, Beatriz...
# ⚠️ TRAMPA CLÁSICA: no usar objetos mutables como defaults
# ❌ Mal — el default [] es compartido entre todas las llamadas
def agregar_item_mal(item, lista=[]):
lista.append(item)
return lista
print(agregar_item_mal("a")) # ["a"]
print(agregar_item_mal("b")) # ["a", "b"] ← ¡el mismo objeto!
print(agregar_item_mal("c")) # ["a", "b", "c"] ← se acumula
# ✅ Bien — usar None como centinela
def agregar_item_bien(item, lista=None):
if lista is None:
lista = []
lista.append(item)
return lista
print(agregar_item_bien("a")) # ["a"]
print(agregar_item_bien("b")) # ["b"] ← lista nueva cada vez
Hola, Ana!
Buenos días, Carlos!
Hola, Beatriz...
['a']
['a', 'b']
['a', 'b', 'c']
['a']
['b']Los valores por defecto se evalúan una sola vez cuando Python define la función, no cada vez que se llama. Si usas un objeto mutable (lista, dict, set) como default, todas las llamadas comparten el mismo objeto. Usa siempre None y crea el objeto dentro de la función.
*args — posicionales variables
*args captura cualquier número de argumentos posicionales en una tupla:
# *args recibe los argumentos extra como tupla
def sumar(*numeros):
print(f"Recibí: {numeros}") # siempre es tupla
return sum(numeros)
print(sumar(1, 2)) # 3
print(sumar(1, 2, 3, 4, 5)) # 15
print(sumar()) # 0
# Combinado con parámetros normales
def registrar(evento, *detalles):
print(f"[{evento}]", *detalles)
registrar("LOGIN", "usuario: ana", "ip: 192.168.1.1")
registrar("ERROR", "archivo no encontrado")
registrar("INFO")
# Desempaquetar una lista como argumentos posicionales con *
numeros = [10, 20, 30]
print(sumar(*numeros)) # equivale a sumar(10, 20, 30)
Recibí: (1, 2)
3
Recibí: (1, 2, 3, 4, 5)
15
Recibí: ()
0
[LOGIN] usuario: ana ip: 192.168.1.1
[ERROR] archivo no encontrado
[INFO]
60**kwargs — keyword variables
**kwargs captura cualquier número de keyword arguments en un diccionario:
# **kwargs recibe los keyword args extra como dict
def registrar_evento(tipo, **metadatos):
print(f"Tipo: {tipo}")
for clave, valor in metadatos.items():
print(f" {clave}: {valor}")
registrar_evento(
"COMPRA",
usuario="ana@ejemplo.com",
monto=99.99,
producto="Teclado mecánico",
metodo_pago="tarjeta"
)
# Desempaquetar un dict como keyword args con **
datos = {"nombre": "Ana", "edad": 28, "ciudad": "Madrid"}
def crear_perfil(nombre, edad, ciudad):
return f"{nombre}, {edad}, {ciudad}"
print(crear_perfil(**datos)) # desempaqueta el dict como kwargs
Tipo: COMPRA
usuario: ana@ejemplo.com
monto: 99.99
producto: Teclado mecánico
metodo_pago: tarjeta
Ana, 28, MadridMezclar todo: el orden correcto
Cuando combinas todos los tipos de parámetros, el orden es obligatorio:
# Orden obligatorio:
# 1. Parámetros posicionales normales
# 2. *args (posicionales variables)
# 3. Parámetros solo-keyword (después de *)
# 4. **kwargs (keyword variables)
def funcion_completa(pos1, pos2, *args, kw_only, **kwargs):
print(f"Posicionales: {pos1}, {pos2}")
print(f"Extra posicionales: {args}")
print(f"Solo keyword: {kw_only}")
print(f"Extra keyword: {kwargs}")
funcion_completa(
1, 2, # pos1, pos2
3, 4, 5, # va a *args
kw_only="obligatorio",
extra1="a",
extra2="b" # van a **kwargs
)
Posicionales: 1, 2
Extra posicionales: (3, 4, 5)
Solo keyword: obligatorio
Extra keyword: {'extra1': 'a', 'extra2': 'b'}*args y **kwargs son nombres convencionales, no palabras clave. Puedes usar *numeros o **opciones. Lo que importa es el * y el **. Sin embargo, respetar la convención mejora la legibilidad del código.
Practica
Cinco ejercicios para dominar los parámetros flexibles de Python.