Imagina que estás desarrollando una aplicación web y necesitas registrar cada llamada a la API, verificar los derechos de acceso de los usuarios y medir el tiempo de ejecución de las funciones. Por supuesto, puedes copiar el mismo código en cada función, pero hay una forma más elegante: los decoradores. Esta es una de las herramientas más potentes de Python, que permite modificar el comportamiento de las funciones sin cambiar su código fuente.
¿Qué son los decoradores?
Un decorador es una función que toma otra función como argumento y devuelve una nueva función con funcionalidad extendida. ¿Suena complicado? En realidad, todo es más fácil de lo que parece.
def my_decorator(func):
def wrapper():
print("Something happens before the function is called")
func()
print("Something happens after calling the function")
return wrapper
@my_decorator
def say_hello():
print("Hi!")
say_hello()Resultado de la ejecución:
Что-то происходит до вызова функции
Привет!
Что-то происходит после вызова функцииEl símbolo @ es un azúcar sintáctico de Python. La entrada @my_decorator antes de la función es equivalente a say_hello = my_decorator(say_hello).
Decoradores con argumentos
Pero, ¿qué pasa si nuestra función toma parámetros? Para ello, utilizamos *args y **kwargs:
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hi, {name}!")
greet("Alexey")Conclusión:
Привет, Алексей!
Привет, Алексей!
Привет, Алексей!Aquí hemos creado un decorador con un parámetro que repite la llamada a la función un número determinado de veces.

Ejemplos prácticos de decoradores
Medición del tiempo de ejecución
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"The {func.__name__} function was executed in {end - start:.4f} seconds")
return result
return wrapper
@timer
def heavy_computation():
time.sleep(2)
return "Done"
heavy_computation()Presta atención a @wraps(func): es un decorador integrado del módulo functools que almacena los metadatos de la función original (nombre, documentación, etc.).
Almacenamiento en caché de resultados
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(100)) # Se ejecutará instantáneamente gracias al almacenamiento en cachéVerificación de permisos
def require_auth(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated'):
raise PermissionError("Authorization required")
return func(user, *args, **kwargs)
return wrapper
@require_auth
def delete_account(user, account_id):
print(f"Account {account_id} has been deleted")
user = {'is_authenticated': True, 'username': 'admin'}
delete_account(user, 123)Cadenas de decoradores
Los decoradores se pueden combinar aplicando varias funciones a una:
@timer
@memoize
def complex_calculation(x, y):
time.sleep(1)
return x ** y
# La primera llamada tardará ~1 segundo
complex_calculation(2, 10)
# La segunda llamada será instantánea (el resultado se almacena en caché)
complex_calculation(2, 10)Los decoradores se aplican de abajo hacia arriba, es decir, primero memoize, luego timer.
Las clases como decoradores
Los decoradores no tienen que ser funciones. Se pueden utilizar clases:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Call #{self.count} of the {self.func.__name__} function"_}")
return self.func(*args, **kwargs)
@CountCalls
def process_data():
print("Processing data...")
process_data()
process_data()
process_data()
Decoradores integrados de Python
Python proporciona varios decoradores integrados útiles:
@property— convierte el método en un atributo de clase@staticmethod— crea un método estático@classmethod— crea un método de clase@functools.lru_cache— almacenamiento en caché con restricción de tamaño@dataclass- creación automática de métodos de clase
from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_function(param):
# Cálculos complejos
return param * 2Cuándo usar decoradores
Los decoradores son ideales para tareas que deben aplicarse a muchas funciones: registro, validación de datos de entrada, manejo de excepciones, control de acceso, almacenamiento en caché, medición de rendimiento, lógica de reintento para errores de red. Ayudan a cumplir con el principio DRY (Don't Repeat Yourself) y hacen que el código sea más limpio y claro.
Problemas potenciales
A pesar de todo el poder de los decoradores, hay que recordar algunos puntos. El uso excesivo de decoradores puede dificultar la depuración del código. Los decoradores añaden pequeños gastos generales al rendimiento. Es importante usar siempre @wraps para guardar los metadatos de la función. Las cadenas de muchos decoradores pueden reducir la legibilidad del código.
Currículum
Los decoradores son una forma elegante de modificar y ampliar la funcionalidad del código sin cambiarlo. Son ampliamente utilizados en marcos populares como Flask y Django, y comprender cómo funcionan hará tu vida como desarrollador mucho más fácil. Empieza con decoradores simples para registrar o medir el tiempo, y gradualmente encontrarás más y más situaciones en las que serán útiles.
¿Quieres dominar los decoradores y otras características avanzadas de Python en la práctica?
Anexo Kodik creado específicamente para que puedas aprender a programar a un ritmo cómodo, con explicaciones claras en ruso y ejemplos reales de la práctica. Analizamos temas complejos en un lenguaje sencillo, desde los conceptos básicos hasta las técnicas profesionales. Cada lección está estructurada de tal manera que no solo memorices la sintaxis, sino que entiendas cómo aplicar los conocimientos en proyectos reales.
Y si tienes alguna pregunta durante el proceso de aprendizaje, te damos la bienvenida a nuestro Canal de Telegram! Es una comunidad viva de desarrolladores donde puedes hacer cualquier pregunta y obtener una respuesta detallada. Nuestro ambiente es amigable e informativo: todos los días analizamos los principales temas en desarrollo, compartimos experiencias y nos ayudamos mutuamente a crecer. ¡Únete a Kodik y comienza tu viaje en la programación con aquellos que realmente entienden cómo hacer que el aprendizaje sea efectivo e interesante!
