Qué son los decoradores en Python
Si llevas más de una semana programando en Python, seguro has visto el símbolo @ sobre una función. Eso es un decorador, una herramienta poderosa y elegante de programación.
Los decoradores en Python permiten "envolver" una función dentro de otra. Modifican o extienden el comportamiento de una función sin alterar su código original. Se usan en todas partes: Flask, Django, FastAPI, pytest, e incluso en la biblioteca estándar de Python.
Cómo funcionan los decoradores
Fundamentos de los decoradores
Un decorador es una función que recibe otra función y devuelve una nueva función o la original modificada.
def decorador(func):
def wrapper():
print("Antes de la llamada")
func()
print("Después de la llamada")
return wrapper
@decorador
def saludar():
print("¡Hola!")
saludar()
Resultado:
Antes de la llamada
¡Hola!
Después de la llamada
El símbolo @decorador es azúcar sintáctico para escribir:
saludar = decorador(saludar)
Estructura interna de los decoradores
Para entender los decoradores en Python, necesitas conocer estos conceptos clave:
- En Python, las funciones son objetos de primera clase
- Las funciones se pueden pasar como argumentos a otras funciones
- Dentro de las funciones se pueden definir otras funciones
- Existe el mecanismo de cierre (closure)
Plantilla universal de un decorador
La plantilla mínima de un decorador es la siguiente:
def decorador(func):
def wrapper(*args, **kwargs):
# Acciones antes de ejecutar la función
resultado = func(*args, **kwargs)
# Acciones después de ejecutar la función
return resultado
return wrapper
Ejemplos prácticos de decoradores
Decorador para medir tiempo de ejecución
import time
def temporizador(func):
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = func(*args, **kwargs)
tiempo_ejecucion = time.time() - inicio
print(f"{func.__name__} tardó {tiempo_ejecucion:.4f} seg")
return resultado
return wrapper
@temporizador
def funcion_lenta():
time.sleep(1)
return "Operación completada"
funcion_lenta()
Este decorador es útil para medir el rendimiento de funciones y optimizar código.
Decorador de verificación de autenticación
def requerir_admin(func):
def wrapper(usuario, *args, **kwargs):
if usuario != "admin":
raise PermissionError("Acceso denegado")
return func(usuario, *args, **kwargs)
return wrapper
@requerir_admin
def eliminar_base_datos(usuario):
print("¡Base de datos eliminada!")
return True
eliminar_base_datos("admin") # Ejecución exitosa
Este decorador mejora la seguridad de la aplicación al controlar el acceso a funciones críticas.
Decorador de registro (logging)
import logging
from functools import wraps
def registrar_llamadas(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"Llamada a función {func.__name__} con argumentos {args}, {kwargs}")
try:
resultado = func(*args, **kwargs)
logging.info(f"Función {func.__name__} ejecutada correctamente")
return resultado
except Exception as e:
logging.error(f"Error en función {func.__name__}: {e}")
raise
return wrapper
Decoradores con argumentos
Creación de una fábrica de decoradores
Cuando un decorador necesita recibir parámetros, se crea una fábrica de decoradores:
def repetir(n):
def decorador(func):
def wrapper(*args, **kwargs):
resultados = []