Los decoradores en Python son una de las características más elegantes y potentes del lenguaje. Permiten modificar o extender el comportamiento de funciones y clases sin alterar su código. Frameworks como Flask, FastAPI y Django los usan de forma masiva. En esta guía aprenderás qué son, cómo funcionan y cómo crear tus propios decoradores Python con ejemplos reales.
¿Qué es un decorador en Python?
Un decorador es una función que recibe otra función como argumento, añade funcionalidad adicional y devuelve una nueva función. La sintaxis @nombre_decorador es azúcar sintáctica: aplicar @mi_decorador sobre una función es equivalente a escribir funcion = mi_decorador(funcion).
def mi_decorador(func):
def wrapper(*args, **kwargs):
print("Antes de ejecutar")
resultado = func(*args, **kwargs)
print("Despues de ejecutar")
return resultado
return wrapper
@mi_decorador
def saludar(nombre):
print(f"Hola, {nombre}!")
saludar("Python")
# Antes de ejecutar
# Hola, Python!
# Despues de ejecutar
Decorador de logging
import functools
import logging
logging.basicConfig(level=logging.INFO)
def log_llamada(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"Llamando a {func.__name__} con args={args}, kwargs={kwargs}")
resultado = func(*args, **kwargs)
logging.info(f"{func.__name__} devolvio {resultado}")
return resultado
return wrapper
@log_llamada
def dividir(a, b):
return a / b
dividir(10, 2) # INFO: Llamando a dividir con args=(10, 2)...
Decorador de caché manual
def cache(func):
almacen = {}
@functools.wraps(func)
def wrapper(*args):
if args not in almacen:
almacen[args] = func(*args)
return almacen[args]
return wrapper
@cache
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(40)) # Rapido gracias a la cache
Decorador con argumentos
Para que un decorador acepte sus propios argumentos, necesitas una capa extra de anidamiento.
def repetir(veces):
def decorador(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(veces):
resultado = func(*args, **kwargs)
return resultado
return wrapper
return decorador
@repetir(3)
def saludar():
print("Hola!")
saludar() # Imprime "Hola!" tres veces
Decorador de validación
def validar_positivos(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for arg in args:
if isinstance(arg, (int, float)) and arg < 0:
raise ValueError(f"Argumento negativo no permitido: {arg}")
return func(*args, **kwargs)
return wrapper
@validar_positivos
def raiz_cuadrada(n):
return n ** 0.5
print(raiz_cuadrada(16)) # 4.0
# raiz_cuadrada(-4) # ValueError
Decorador de clase — Singleton
def singleton(cls):
instancias = {}
@functools.wraps(cls)
def obtener_instancia(*args, **kwargs):
if cls not in instancias:
instancias[cls] = cls(*args, **kwargs)
return instancias[cls]
return obtener_instancia
@singleton
class Configuracion:
def __init__(self):
self.modo = "produccion"
c1 = Configuracion()
c2 = Configuracion()
print(c1 is c2) # True — misma instancia
Decoradores estándar de Python
@property— convierte un método en atributo de solo lectura@classmethod— método que recibe la clase como primer argumento@staticmethod— método sin acceso a instancia ni clase@functools.lru_cache— caché automática con límite configurable@dataclasses.dataclass— genera__init__,__repr__y__eq__automáticamente
Preguntas frecuentes sobre decoradores en Python
¿Qué es exactamente un decorador en Python?
Un decorador es una función que toma otra función como argumento, añade funcionalidad y devuelve una nueva función. La sintaxis @nombre_decorador es azúcar sintáctica: escribir @mi_decorador sobre def funcion() equivale a funcion = mi_decorador(funcion). Son la base de frameworks como Flask (rutas con @app.route), FastAPI y Django REST Framework.
¿Para qué sirve @functools.wraps?
@functools.wraps preserva los metadatos de la función original (nombre __name__, docstring __doc__ y firma) cuando creas un wrapper. Sin él, la función decorada pierde su identidad, lo que complica el debugging y la introspección. Siempre debes añadir @functools.wraps(func) justo antes de definir la función wrapper dentro de tu decorador.
¿Cómo se aplican múltiples decoradores a una función?
Se apilan verticalmente y se aplican de abajo hacia arriba. Si escribes @decorador_b encima de @decorador_a sobre una función, primero se aplica decorador_a y luego decorador_b sobre el resultado. El orden importa: por ejemplo, poner @log antes de @validar registrará incluso las llamadas con argumentos inválidos antes de que falle la validación.
¿Cuál es la diferencia entre un decorador de función y uno de clase?
Un decorador de función envuelve funciones individuales añadiéndoles comportamiento. Un decorador de clase recibe una clase entera y puede modificar sus atributos, añadir métodos o devolver una clase diferente. Ejemplos en la librería estándar: @dataclass añade __init__ y __repr__ automáticamente, y el patrón @singleton garantiza que solo exista una instancia de la clase.
Conclusión
Los decoradores hacen el código Python más limpio y reutilizable. Sigue aprendiendo con nuestra guía de POO en Python, los ejercicios con funciones y el tutorial de FastAPI donde los decoradores son protagonistas.