Decoradores en Python: Mejorando Funciones sin Modificarlas

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)

Etiquetas: Python decoradores funciones closures functools

Publicado el 6-14 07:20