Decoradores en Python
Los decoradores son una característica poderosa de Python que te permite modificar o extender el comportamiento de funciones o clases sin cambiar su código directamente.
Concepto Básico
Un decorador es una función que toma otra función como argumento y devuelve una nueva función, usualmente extendiendo o modificando el comportamiento de la función original.
Sintaxis Básica
@mi_decorador
def mi_funcion():
passEs equivalente a:
def mi_funcion():
pass
mi_funcion = mi_decorador(mi_funcion)Crear un Decorador Simple
Ejemplo 1: Decorador para medir tiempo de ejecución
import time
def medir_tiempo(funcion):
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = funcion(*args, **kwargs)
fin = time.time()
print(f"Tiempo de ejecución: {fin - inicio:.4f} segundos")
return resultado
return wrapper
@medir_tiempo
def esperar_segundos(segundos):
time.sleep(segundos)
return f"Esperé {segundos} segundos"
# Uso
resultado = esperar_segundos(2)
# Output: Tiempo de ejecución: 2.0023 segundos
print(resultado) # Output: Esperé 2 segundosEjemplo 2: Decorador para verificar login
def requiere_login(funcion):
def wrapper(usuario, *args, **kwargs):
if usuario.get('autenticado', False):
return funcion(usuario, *args, **kwargs)
else:
return "Error: Usuario no autenticado"
return wrapper
@requiere_login
def acceder_recurso_secreto(usuario):
return f"Bienvenido {usuario['nombre']} al recurso secreto"
# Uso
usuario1 = {'nombre': 'Ana', 'autenticado': True}
usuario2 = {'nombre': 'Juan', 'autenticado': False}
print(acceder_recurso_secreto(usuario1)) # Bienvenido Ana al recurso secreto
print(acceder_recurso_secreto(usuario2)) # Error: Usuario no autenticadoDecoradores con Argumentos
Ejemplo 3: Decorador con parámetros
def repetir(n_veces):
def decorador(funcion):
def wrapper(*args, **kwargs):
for i in range(n_veces):
resultado = funcion(*args, **kwargs)
return resultado
return wrapper
return decorador
@repetir(n_veces=3)
def saludar(nombre):
print(f"Hola {nombre}")
saludar("María")
# Output:
# Hola María
# Hola María
# Hola MaríaEjemplo 4: Decorador para validar argumentos
def validar_tipo(tipo_esperado):
def decorador(funcion):
def wrapper(*args, **kwargs):
# Validar primer argumento
if args and not isinstance(args[0], tipo_esperado):
raise TypeError(f"Se esperaba {tipo_esperado}, se recibió {type(args[0])}")
return funcion(*args, **kwargs)
return wrapper
return decorador
@validar_tipo(int)
def duplicar(numero):
return numero * 2
print(duplicar(5)) # Output: 10
# print(duplicar("5")) # Esto lanzaría TypeErrorMúltiples Decoradores
Puedes aplicar múltiples decoradores a una función:
def decorador1(funcion):
def wrapper():
print("Decorador 1 - antes")
funcion()
print("Decorador 1 - después")
return wrapper
def decorador2(funcion):
def wrapper():
print("Decorador 2 - antes")
funcion()
print("Decorador 2 - después")
return wrapper
@decorador1
@decorador2
def mi_funcion():
print("Función original")
mi_funcion()
# Output:
# Decorador 1 - antes
# Decorador 2 - antes
# Función original
# Decorador 2 - después
# Decorador 1 - despuésDecoradores de Clases
También puedes usar decoradores con clases:
Ejemplo 5: Decorador para clases
def agregar_metodo(cls):
def nuevo_metodo(self):
return f"Soy un método agregado a {self.__class__.__name__}"
cls.metodo_agregado = nuevo_metodo
return cls
@agregar_metodo
class MiClase:
def __init__(self, nombre):
self.nombre = nombre
objeto = MiClase("Test")
print(objeto.metodo_agregado()) # Soy un método agregado a MiClaseDecoradores Built-in de Python
Python incluye algunos decoradores útiles:
@staticmethod y @classmethod
class Calculadora:
@staticmethod
def sumar(a, b):
return a + b
@classmethod
def restar(cls, a, b):
return a - b
print(Calculadora.sumar(5, 3)) # Output: 8
print(Calculadora.restar(5, 3)) # Output: 2@property
class Persona:
def __init__(self, nombre, edad):
self._nombre = nombre
self._edad = edad
@property
def nombre(self):
return self._nombre
@property
def edad(self):
return self._edad
@edad.setter
def edad(self, valor):
if valor >= 0:
self._edad = valor
else:
raise ValueError("La edad no puede ser negativa")
persona = Persona("Ana", 25)
print(persona.nombre) # Ana
print(persona.edad) # 25
persona.edad = 30
print(persona.edad) # 30Decoradores con functools.wraps
Es importante usar functools.wraps para preservar los metadatos de la función original:
import functools
def mi_decorador(funcion):
@functools.wraps(funcion)
def wrapper(*args, **kwargs):
print("Antes de la función")
resultado = funcion(*args, **kwargs)
print("Después de la función")
return resultado
return wrapper
@mi_decorador
def ejemplo():
"""Esta es una función de ejemplo"""
pass
print(ejemplo.__name__) # ejemplo (sin wraps sería 'wrapper')
print(ejemplo.__doc__) # Esta es una función de ejemploCasos de Uso Comunes
Logging y monitoreo
Validación de argumentos
Control de acceso y autenticación
Cache y memoización
Retry en caso de errores
Medición de performance
Ejemplo 6: Decorador para cache
def cache(funcion):
cache_dict = {}
@functools.wraps(funcion)
def wrapper(*args):
if args in cache_dict:
print("Resultado desde cache")
return cache_dict[args]
resultado = funcion(*args)
cache_dict[args] = resultado
print("Resultado calculado")
return resultado
return wrapper
@cache
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1)
print(factorial(5)) # Calcula y cachea
print(factorial(5)) # Usa cacheLos decoradores son una herramienta extremadamente poderosa en Python que te permite escribir código más limpio, modular y reutilizable. Son ampliamente usados en frameworks web como Flask y Django.
Comentarios
Publicar un comentario