Pregunta 1¿Cuál de las siguientes tiene el mismo significado que poner @logger encima de def greet(): ...?
Decoradores — Añadir comportamiento a las funciones con @
@logger equivale a greet = logger(greet). Verás la estructura en tres pasos def → wrapper → return wrapper y el patrón pasamanos con *args / **kwargs que envuelve cualquier firma de función.
Al final de el artículo anterior sobre lambdas, habías visto las principales formas de tratar las funciones como valores. Para cerrar, fijemos los decoradores — la sintaxis dedicada a apilar comportamiento extra encima de una función.
¿Qué es un decorador?
Un decorador es una forma de añadir comportamiento antes y después de una función sin cambiar la función misma. Cosas como «registrar la llamada», «medir el tiempo de ejecución» o «cachear el resultado» — comportamiento compartido que quieres apilar sobre muchas funciones — pueden vivir en un único sitio.
La sintaxis es solo una línea encima de la definición de la función: @nombre_decorador. Python lo lee como «lo mismo que func = nombre_decorador(func)».
@logger encima de def greet(): hace que Python ejecute greet = logger(greet) internamente, reemplazando greet con una función nueva envuelta por logger.# El decorador en sí (una función de orden superior que toma y retorna una función)
def logger(func):
def wrapper():
print("=== inicio ===")
func() # llama a la función original
print("=== fin ===")
return wrapper
# Lado del que llama: solo añade @
@logger
def greet(): # → logger(greet)
print("Hola")
greet()
# === inicio ===
# Hola
# === fin ===
# Equivalente internamente a:
# def greet():
# print("Hola")
# greet = logger(greet)
El decorador básico — Envolver una función con wrapper
El esqueleto de un decorador es una forma de 3 pasos: la función externa toma func, la función interna (por convención wrapper) llama a func(), y haces return wrapper. El wrapper que mantiene func disponible mientras se ejecuta es exactamente una clausura.
Lo que escribas antes y después de func() se ejecuta cada vez que se llama a la función decorada.
def logger(func):
def wrapper(*args, **kwargs):
print(f"[LOG] ejecutando {func.__name__}")
result = func(*args, **kwargs)
print(f"[LOG] {func.__name__} terminado")
return result
return wrapper
@logger
def greet(name):
return f"Hola, {name}"
print(greet("Ana"))
# [LOG] ejecutando greet
# [LOG] greet terminado
# Hola, Ana
- greet se reemplaza por la función wrapper
- El cuerpo original de
greetsigue vivo comofuncdentro dewrapper
funcmantiene lagreetoriginal- Construye
wrapperdentro y la retorna
- Ejecuta pre →
func()→ post en orden - Desde fuera, esta es la nueva
greet
Pasar cualquier argumento con *args / **kwargs
Hasta ahora, wrapper no tomaba argumentos. Cuando quieras decorar funciones que sí toman argumentos, usa *args / **kwargs para aceptar cualquier argumento tal cual y reenviarlos directos a `func`.
Eso convierte al decorador en uno genérico que funciona con cualquier firma de función. Maneja add(2, 3) (posicional) y add(2, 3, name="ABC") (por palabra clave) con el mismo decorador.
def log_call(func):
def wrapper(*args, **kwargs):
print(f"llamada: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs) # desempaqueta y reenvía aquí también
print(f"resultado: {result}")
return result # no olvides retornarlo
return wrapper
@log_call
def add(a, b):
return a + b
print(add(2, 3))
# llamada: args=(2, 3), kwargs={}
# resultado: 5
# 5
print(add(2, b=3))
# llamada: args=(2,), kwargs={'b': 3}
# resultado: 5
# 5
No olvides retornar el resultado
Si wrapper solo escribe result = func(...) y se olvida del return, el valor de retorno de la función decorada se convierte silenciosamente en None. El accidente clásico es encontrar que add(2, 3) retorna None calladamente — cuando escribas un decorador, trata return result como parte de la misma memoria muscular.
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2¿Qué imprime este código?def deco(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs) * 2
return wrapper
@deco
def plus(a, b):
return a + b
print(plus(3, 4))