Ejemplos de decoradores en el mundo real
Decorador de registro (Logging Decorator)
Un caso de uso común de los decoradores es el registro de llamadas a funciones. Esto puede ser útil para depuración, monitoreo o auditoría.
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args} and kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@log_function_call
def add_numbers(a, b):
return a + b
result = add_numbers(2, 3)
print(result)
Esto generará la siguiente salida:
Calling add_numbers with args=(2, 3) and kwargs={}
5
Decorador de caché (Caching Decorator)
Los decoradores también se pueden utilizar para almacenar en caché los resultados de llamadas a funciones costosas, mejorando el rendimiento.
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n <= 1:
return n
else:
return (fibonacci(n-1) + fibonacci(n-2))
print(fibonacci(100))
El decorador lru_cache
del módulo functools
proporciona una forma sencilla de implementar una caché de uso menos reciente (Least Recently Used - LRU) para los resultados de las funciones.
Decorador de control de acceso (Access Control Decorator)
Los decoradores se pueden utilizar para aplicar control de acceso a funciones o métodos, asegurando que solo los usuarios autorizados puedan acceder a cierta funcionalidad.
from functools import wraps
def require_admin(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not is_admin(args[0]):
raise ValueError("Access denied. You must be an admin.")
return func(*args, **kwargs)
return wrapper
class User:
def __init__(self, name, is_admin):
self.name = name
self.is_admin = is_admin
@require_admin
def delete_user(self, user_to_delete):
print(f"Deleting user: {user_to_delete.name}")
admin = User("LabEx", True)
regular_user = User("John", False)
admin.delete_user(regular_user) ## Works
regular_user.delete_user(admin) ## Raises ValueError
En este ejemplo, el decorador require_admin
verifica si el usuario que llama al método delete_user
es un administrador antes de permitir que la operación se realice.
Decorador de reintentos (Retry Decorator)
Los decoradores también se pueden utilizar para implementar un mecanismo de reintentos para funciones que pueden fallar debido a problemas temporales, como errores de red o límites de tasa de la API.
import time
from functools import wraps
def retry(max_retries=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
retries = 0
while retries < max_retries:
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Function {func.__name__} failed. Retrying... ({retries+1}/{max_retries})")
retries += 1
time.sleep(delay)
raise Exception(f"Maximum number of retries ({max_retries}) reached for function {func.__name__}")
return wrapper
return decorator
@retry(max_retries=3, delay=2)
def flaky_function():
## Simulate a flaky function that fails 50% of the time
if random.random() < 0.5:
raise Exception("Oops, something went wrong!")
return "Success!"
print(flaky_function())
En este ejemplo, el decorador retry
reintentará automáticamente la función flaky_function
hasta 3 veces, con un retraso de 2 segundos entre cada intento, antes de lanzar una excepción.
Estos son solo algunos ejemplos de los muchos casos de uso reales de los decoradores en Python. Los decoradores son una herramienta poderosa y flexible que puede ayudarte a escribir código más modular, mantenible y reutilizable.