Decoradores en Python para modificación de funciones

Decoradores para funciones sin parámetros

Los decoradores permiten envolver una función para extender su comportamiento sin alterar su definición original. Un caso típico es medir el tiempo de ejecución de una función.

import time

def evaluar_rendimiento(funcion):
    def envoltura():
        inicio = time.perf_counter()
        resultado = funcion()
        fin = time.perf_counter()
        print(f"Tiempo de ejecución: {fin - inicio:.4f} segundos")
        return resultado
    return envoltura

@evaluar_rendimiento
def proceso_lento():
    time.sleep(1)
    return "Proceso completado"

print(proceso_lento())

Este decorador captura la función original mediante closure y devuelve una nueva función que mantiene la interfaz original pero añade el cálculo de tiempo. La sintaxis con @ permite una aplicación limpia.

Decoradores con argumentos

Cuando se necesita pasar parámetros al propio decorador, se emplea una capa adicional de funciones.

USUARIOS_REGISTRADOS = {"ana": "contraseña123", "pedro": "segura456"}

def verificar_autenticacion(requiere_admin=False):
    def decorador(func):
        def envoltura(usuario, *args, **kwargs):
            if usuario not in USUARIOS_REGISTRADOS:
                return "Acceso denegado: usuario no reconocido"
            if requiere_admin and usuario != "ana":
                return "Se requieren privilegios de administrador"
            return func(usuario, *args, **kwargs)
        return envoltura
    return decorador

@verificar_autenticacion(requiere_admin=False)
def mostrar_perfil(nombre_usuario):
    return f"Perfil del usuario: {nombre_usuario}"

@verificar_autenticacion(requiere_admin=True)
def modificar_sistema(nombre_usuario, accion):
    return f"El usuario {nombre_usuario} ejecutó: {accion}"

print(mostrar_perfil("pedro"))
print(modificar_sistema("pedro", "reinicio"))

La esrtuctura envuelve el decorador original en una función fábrica que acepta los parámetros de configuración y devuelve el decorador real.

Aplicación múltiple de decoradores

Los decoradores se aplican de abajo hacia arriba, pero ejecutan de arriba hacia abajo. Esto permite crear capas de procesamiento.

def registrar_acceso(func):
    def envoltura(usuario, *args, **kwargs):
        print(f"[LOG] Acceso de {usuario}")
        return func(usuario, *args, **kwargs)
    return envoltura

def validar_permisos(nivel_requerido):
    def decorador(func):
        def envoltura(usuario, *args, **kwargs):
            niveles = {"visitante": 1, "editor": 2, "administrador": 3}
            if niveles.get(usuario, 0) < nivel_requerido:
                return "Permisos insuficientes"
            return func(usuario, *args, **kwargs)
        return envoltura
    return decorador

@registrar_acceso
@validar_permisos(nivel_requerido=2)
def editar_contenido(usuario, contenido):
    return f"Contenido modificado por {usuario}: {contenido}"

print(editar_contenido("editor", "Nuevo texto"))
print(editar_contenido("visitante", "Intento de edición"))

La composición de decoradores crea un pipeline donde cada capa puede interceptar y condicionar la ejecución de la función subyacente.

Decoradores con parámetros de fábrica

Se pueden crear sistemas de decoradores configurables mediante funciones fábrica.

def crear_decorador(config):
    def decorador(func):
        def envoltura(*args, **kwargs):
            if config.get("registrar"):
                print(f"Invocando: {func.__name__}")
            if config.get("max_intentos"):
                for intento in range(config["max_intentos"]):
                    try:
                        return func(*args, **kwargs)
                    except Exception as e:
                        if intento == config["max_intentos"] - 1:
                            raise e
            return func(*args, **kwargs)
        return envoltura
    return decorador

@crear_decorador({"registrar": True, "max_intentos": 3})
def operacion_arriesgada(valor):
    import random
    if random.random() < 0.7:
        raise ValueError("Error aleatorio")
    return f"Operación exitosa con valor: {valor}"

print(operacion_arriesgada(42))

Este patrón encapsula la lógica de configuración dentro del decorador, permitiendo un comportamiento altamente personalizable sin multiplicar funciones decoradoras.

Etiquetas: Python decoradores closures funciones-de-orden-superior sintaxis-azucarada

Publicado el 7-2 04:31