Definición
En Python, los decoradores son estructuras sintácticas poderosas que permiten extender o alterar el comportamiento de funciones sin cambiar su código original. Esencialmente, un decorador es una función que toma otra función como entrada y devuelve una nueva función.
Conceptos Básicos de Decoradores
Un ejemplo simple de decorador:
def decorador_basico(funcion):
def envoltura():
print('Antes de la ejecución')
funcion()
print('Después de la ejecución')
return envoltura
@decorador_basico
def saludar():
print("¡Hola!")
saludar()
Salida:
Antes de la ejecución
¡Hola!
Después de la ejecución
Cómo Funcionan los Decoradores
El ejemplo anterior es equivalente a:
def saludar():
print('¡Hola!')
saludar = decorador_basico(saludar)
saludar()
Esto significa que el decorador @decorador_basico pasa la función decorada al decorador y reemplaza la función original con la nueva.
Decoradores para Funciones con Argumentos
Si la función decorada necesita parámetros, la envoltura debe incluir argumentos:
def decorador_adaptable(funcion):
def envoltura(*args, **kwargs):
print('Iniciando proceso')
funcion(*args, **kwargs)
print('Finalizando proceso')
return envoltura
@decorador_adaptable
def mostrar_nombre(nombre):
print(f'¡Hola, {nombre}!')
mostrar_nombre('Ana')
Decoradores con Parámetros (Fábricas de Decoradores)
Para crear decoradores que acepten argumentos, se usa una capa adicional:
def repetir(veces):
def decorador(funcion):
def envoltura(*args, **kwargs):
for _ in range(veces):
resultado = funcion(*args, **kwargs)
return resultado
return envoltura
return decorador
@repetir(3)
def decir_adios():
print("¡Adiós!")
decir_adios()
Salida:
¡Adiós!
¡Adiós!
¡Adiós!
Apilando Múltiples Decoradores
Varios decoradores pueden aplicarse en secuencia, desde el superier hasta el inferior:
def decorador_alfa(funcion):
def envoltura():
print("Entrando en alfa")
funcion()
print("Saliendo de alfa")
return envoltura
def decorador_beta(funcion):
def envoltura():
print("Entrando en beta")
funcion()
print("Saliendo de beta")
return envoltura
@decorador_alfa
@decorador_beta
def tarea():
print("Ejecutando tarea")
tarea()
Análisis de Ejecución:
Equivalente a: tarea = decorador_alfa(decorador_beta(tarea))
Salida:
Entrando en alfa
Entrando en beta
Ejecutando tarea
Saliendo de beta
Saliendo de alfa
Preservando Metadatos con functools.wraps
Al usar decoradores, los metadatos de la función (como __name__, __doc__) se pierden. Para retenerlos, se emplea functools.wraps:
from functools import wraps
def decorador_seguro(funcion):
@wraps(funcion)
def envoltura(*args, **kwargs):
'''Función envuelta en el decorador'''
print("Operación previa")
resultado = funcion(*args, **kwargs)
print("Operación posterior")
return resultado
return envoltura
@decorador_seguro
def demo():
"""Esta es una función de demostración"""
print("Función demo en ejecución")
print(demo.__name__) # Salida: demo (no envoltura)
print(demo.__doc__) # Salida: Esta es una función de demostración (no la del decorador)