Técnicas y Casos de Uso Avanzados de los Decoradores
Como has visto, los decoradores son una herramienta poderosa para modificar el comportamiento de las funciones en Python. En esta sección, exploraremos algunas técnicas y casos de uso más avanzados de los decoradores.
Decorando Clases
Los decoradores también se pueden utilizar para modificar el comportamiento de las clases. Aquí tienes un ejemplo de un decorador que agrega un método de registro (logging) a una clase:
def log_class_methods(cls):
class LoggedClass(cls):
def __getattribute__(self, attr):
if callable(super(LoggedClass, self).__getattribute__(attr)):
def logged_method(*args, **kwargs):
print(f"Calling method {attr}")
return super(LoggedClass, self).__getattribute__(attr)(*args, **kwargs)
return logged_method
return super(LoggedClass, self).__getattribute__(attr)
return LoggedClass
@log_class_methods
class MyClass:
def __init__(self, value):
self.value = value
def do_something(self):
print(f"Doing something with value: {self.value}")
obj = MyClass(42)
obj.do_something() ## Output: Calling method do_something
## Doing something with value: 42
En este ejemplo, el decorador log_class_methods
toma una clase como argumento y devuelve una nueva clase que envuelve todos los métodos de la clase original con una función de registro.
Decoradores con Estado
Los decoradores también pueden mantener un estado entre las llamadas a la función. Esto puede ser útil para el almacenamiento en caché (caching), la limitación de tasa (rate limiting) u otras operaciones con estado. Aquí tienes un ejemplo de un decorador que almacena en caché los resultados de una función:
def cache(func):
cache = {}
def wrapper(*args):
if args in cache:
print("Returning cached result")
return cache[args]
else:
result = func(*args)
cache[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) ## Output: Calculating fibonacci(10)
## 55
print(fibonacci(10)) ## Output: Returning cached result
## 55
En este ejemplo, el decorador cache
mantiene un diccionario de los argumentos de la llamada a la función y sus resultados correspondientes. Cuando se llama a la función decorada, el decorador primero verifica si el resultado ya está almacenado en caché y, si es así, devuelve el resultado almacenado. De lo contrario, calcula el resultado y lo almacena en la caché para su uso futuro.
Fábricas de Decoradores
A veces, es posible que desees crear decoradores que se puedan configurar con argumentos. Esto se puede lograr utilizando una fábrica de decoradores, que es una función que devuelve un decorador. Aquí tienes un ejemplo:
def repeat(n):
def decorator(func):
def wrapper():
result = ""
for _ in range(n):
result += func()
return result
return wrapper
return decorator
@repeat(3)
def say_hello():
return "hello "
print(say_hello()) ## Output: hello hello hello
En este ejemplo, la función repeat
es una fábrica de decoradores que toma un argumento n
y devuelve un decorador que envuelve la función original, la llama n
veces y concatena los resultados.
Estas técnicas avanzadas de decoradores demuestran la flexibilidad y el poder de los decoradores en Python. Al utilizar decoradores, puedes crear código reutilizable, modular y fácil de mantener que se puede extender y personalizar fácilmente para satisfacer tus necesidades.