Los decoradores son funciones que modifican o
amplían el comportamiento de otras funciones, sin necesidad de modificar el código original
de estas funciones. En otras palabras, los decoradores permiten envolver
una función y agregarle funcionalidad extra. Son una característica poderosa y elegante en
Python, y si alguna vez has visto el símbolo @, seguramente has trabajado con
un decorador.
Un decorador es una función que toma otra función como entrada y devuelve una nueva función. En lugar de llamar a la función directamente, la función decorada será ejecutada con el comportamiento adicional que el decorador define.
def mi_decorador(func):
def envoltorio():
print("Antes de la función")
func()
print("Después de la función")
return envoltorio
@mi_decorador
def suma():
print("Sumando...")
suma()
mi_decorador es la función decoradora.suma es la función original.envoltorio es la función interna que envuelve
y modifica el comportamiento de suma.La línea @mi_decorador es una forma de aplicar el
decorador sobre la función suma.
En Python, las funciones son objetos de primera clase, lo que significa que puedes:
Esto es importante porque los decoradores se definen como funciones que reciben otras funciones como argumentos y devuelven una nueva función.
def di_hola():
print("¡Hola!")
saludo = di_hola
saludo() # Imprime "¡Hola!"
Aunque lo más común es usar el símbolo @, puedes definir y aplicar un decorador
de manera manual:
def mi_decorador(func):
def envoltorio():
print("Función decorada")
return func()
return envoltorio
def suma():
print("Sumando...")
decorada = mi_decorador(suma)
decorada() # Imprime "Función decorada" seguido de "Sumando..."
Esto hace lo mismo que el uso de @mi_decorador, pero aplicado manualmente.
A veces es útil que un decorador reciba parámetros adicionales. Para hacerlo, necesitamos envolver el decorador de nuevo en una función que acepte estos parámetros. Aquí te mostramos un ejemplo:
def repetir_veces(n):
def decorador(func):
def envoltorio(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return envoltorio
return decorador
@repetir_veces(3)
def saludo():
print("¡Hola!")
saludo() # Imprime "¡Hola!" tres veces
En este caso, repetir_veces es un decorador que toma el parámetro
n para repetir la función decorada un número de veces.
Un uso común de los decoradores es registrar (loggear) las
actividades de ciertas funciones. A continuación, tenemos un decorador log que
escribe el resultado de una función en un archivo:
import datetime
def log(func):
def envoltorio(*args, **kwargs):
resultado = func(*args, **kwargs)
with open("log.txt", "a") as f:
f.write(f"{datetime.datetime.now()} - {func.__name__} fue llamada y retornó: {resultado}\n")
return resultado
return envoltorio
@log
def suma(a, b):
return a + b
print(suma(3, 4)) # Guarda en el archivo log.txt la información sobre esta ejecución
En aplicaciones web, especialmente en frameworks como Flask, los decoradores son muy útiles para gestionar la autenticación. Aquí tienes un ejemplo simplificado de un decorador que verifica si un usuario está autenticado antes de permitirle acceder a una función:
def requiere_autenticacion(func):
def envoltorio(*args, **kwargs):
if not autenticado:
raise PermissionError("No estás autenticado")
return func(*args, **kwargs)
return envoltorio
autenticado = False # Cambia a True para permitir el acceso
@requiere_autenticacion
def acceder_recurso():
print("Recurso accedido")
# Intento de acceso
try:
acceder_recurso()
except PermissionError as e:
print(e) # Imprime "No estás autenticado"