Apuntes_Python/02_conceptos/13_decorators
2024-01-16 12:23:41 -03:00
..
decoradores_clas.py init Apuntes Python 2022-12-24 22:41:20 -03:00
decoradores_func.py init Apuntes Python 2022-12-24 22:41:20 -03:00
README.md ed: domain links on files <kickto.net >letz.dev 2024-01-16 12:23:41 -03:00

Decoradores

Decoradores de Funciones

Son los mas comunes, es una función que toma otra función como argumento, y extiende
la funcionalidad de esta sin modificarla. Permite nueva funcionalidad a una funcion existente.
ej.

    import functools
    
    def mi_decorador(func):    
        @functools.wraps(func)
        def funcion_envoltura(*args, **kwargs):
            # hacer algo...
            result func(*args, **kwargs)
            # hacer algo...
            return result
        return funcion_envoltura


    @mi_decorador
    def una_funcion():
        pass

Las funciones en Python son objetos de primera clase, es decir, como cualquier otro objeto,
estas pueden ser definidas dentro de una función, pasarse como argumento a una función, o
ser devueltas por una función.

Usos típicos:

Implementacion de timers de ejecución.
Decoradores debug para obtener información adicional de la función que llama al decorador y sus arguentos.
Como comprobación de agumentos que son requeridos para el correcto uso de alguna función. Registrar funciones como plug-ins con decoradores.

Ejemplo basico

import functools

def inicio_final_decorador(func):
    def envoltorio():
        print("\nInicio")
        func()
        print("Final")
    return envoltorio

@inicio_final_decorador
def imprime_nombre():
    print("Pepe")

#imprime_nombre = inicio_final_decorador(imprime_nombre)
imprime_nombre()
#   Inicio
#   Pepe
#   Final

Decorador con argumento (*args, *kwargs)

import functools


def mi_deco(func):
    @functools.wraps(func)
    def agregado(*args):
        """
        Fución que envuelve a la función pasada como argumento
        """
        print("\nSumando 5")
        func(*args)
        print("Fin")
    return agregado

@mi_deco
def add5(x):
    """
    Fución que suma 5 a número pasado como argumento
    """
    print(x + 5)

add5(4)


# Identidad de la función
print(help(add5))
print(add5.__name__)
"""
    Esto se corrige con el módulo functools
Help on function agregado in module __main__:

agregado(*args)
    Fución que envuelve a la función pasada como argumento
"""
"""
    usando @functools.wraps(func)

Help on function add5 in module __main__:

add5(x)
    Fución que suma 5 a número pasado como argumento
(END)
"""

Decorador con argumento

def repetir(veces):
    def decorador_repetir(func):
        @functools.wraps(func)
        def envoltorio(*args, **kwargs):
            for _ in range(veces):
                result = func(*args, **kwargs)
            return result
        return envoltorio
    return decorador_repetir

@repetir(veces=3)
def saludo(nombre):
    print(f'Hola {nombre}')

saludo('Roobeeerrrt')

Decoradores anidados

def decor_ini_fin(func):
    @functools.wraps(func)
    def envolt(*args, **kwargs):
        print('\nInicio')
        result = func(*args, **kwargs)
        print('Fin')
        return result
    return envolt


def debug(func):
    @functools.wraps(func)
    def envoltorio(*args, **kwargs):
        args_repr = [repr(a) for a in args]
        kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
        firma = ", ".join(args_repr + kwargs_repr)
        print(f"Llamado a {func.__name__}({firma})")
        result = func(*args, **kwargs)
        print(f"{func.__name__!r} returned {result!r}")
        return result
    return envoltorio

@debug
@decor_ini_fin
def di_hola(nombre):
    saludo = f'Hola {nombre}'
    print(saludo)
    return saludo

di_hola('Fernando')

Decoradores de Clases

Ejemplo contador de llamadas

class ContadLLamada:
    def __init__(self, func):
        self.func = func
        self.num_llamadas = 0

    def __call__(self, *args, **kwargs):
        self.num_llamadas += 1
        print(f'Se ha ejecutado {self.num_llamadas} veces')
        return self.func(*args, **kwargs)


@ContadLLamada
def saluda():
    print('Hola')

@ContadLLamada
def despide():
    print('Chao')

saluda()
#   Se ha ejecutado 1 veces
#   Hola

saluda()
#   Se ha ejecutado 2 veces
#   Hola

saluda()
#   Se ha ejecutado 3 veces
#   Hola

despide()
#   Se ha ejecutado 1 veces
#   Chao

saluda()
#   Se ha ejecutado 4 veces
#   Hola

despide()
#   Se ha ejecutado 2 veces
#   Chao

despide()
#   Se ha ejecutado 3 veces
#   Chao