Return
Mar 1, 2025

Decorators in Python

This is the first post of my new Astro blog.

Python

Decorators in Python are a powerful and elegant way to modify or extend the behavior of a function or class without altering its original implementation. They work by wrapping a function or class with another function that adds additional functionality before or after execution.

To understand how decorators work, we first need to grasp the concept of closures.

Closures in Python

Una función que guarda referencias a variables locales en su entorno de definición, incluso cuando la función ha terminado de ejecutarse. Esto significa que la función puede acceder y recordar los valores de esas variables aunque ya no existan en la memoria. El concepto de closure se basa en la capacidad de Python de tratar las funciones como objetos de primera clase, lo que permite que las funciones sean asignadas a variables, pasadas como argumentos y retornadas como valores.

def make_multiplier_of(n):
	def multiplier(x):
		return x * n
	return multiplier

times5 = make_multiplier_of(5)

print(times5(3))
# Output: 15

La función make_multiplier_of toma de argumento un número n y devuelve la función interna multiplier, el cual recuerda el valor de n, incluso después de eliminar la función make_multiplier_of. Pudiendo así hacer cálculos con la variable inicial n y pudiendo recibir mas y.

TIP

Recuerda que la función principal siempre debe retornar la función anidada.

TIP

Los closures son útiles cuando se desea tener funciones que “recuerden” ciertos valores o estados específicos en un momento dado.

def contador():
    count = 0
    def incrementar():
        nonlocal count
        count += 1
        print(f"Contador: {count}")
    return incrementar

contador1 = contador()
contador1()  # Output: Contador: 1
contador1()  # Output: Contador: 2

contador2 = contador()
contador2()  # Output: Contador: 1

@decorando

Los decoradores aprovechan los closures para envolver funciones o clases y modificar su comportamiento agregando funcionalidad adicional antes o después de su ejecución, sin modificar su implementación original.

Al colocar un decorador encima de una función o clase, la función del decorador se ejecuta antes de la función o clase decorada. El decorador puede realizar acciones adicionales, modificar los argumentos o el resultado y luego llamar a la función o clase original.

TIP

Un decorador es una función que recibe como parámetro otra función, le añade cosas, y retorna una función distinta.

def decorador(func):
	def envoltura(name):
		print(f'Hola {name} desde el decorador')
		func(name)
		print("Se ha ejecutado la función")
	return envoltura

TIP

La primera función recibe siempre la función a decorar, la segunda recibe los argumentos que la función a decorar puede recibir y dentro de la misma puede hacer cálculos antes o después de ejecutarla. Luego la primera función retorna la segunda función.

Existen 2 formas de aplicar este decorador, sin usar @

# Sin usar @
def despedirse(name):
	print(f"Adios {name}")

despedirse = decorador(despedirse)
despedirse("Renzo")

O usando @ en la cabeza de la función a decorar

@decorador
def saludar(name):
	print(f"Hola {name}")

saludar("Renzo")

Agregando argumentos

Para crear un decorador que reciba parámetros, se utiliza una función adicional que envuelve al decorador principal. Esta función toma los parámetros y devuelve el decorador.

def decorador_con_parametros(parametro1, parametro2):
    def decorador(funcion):
        def wrapper(*args, **kwargs):
            # Acciones adicionales utilizando los parámetros
            print(f"Parámetros {parametro1} - {parametro2}")
            return funcion(*args, **kwargs)
        return wrapper
    return decorador

Se puede utilizar de la siguiente manera:

@decorador_con_parametros("Hola", 10)
def saludar(nombre):
    print(f"Hola, {nombre}!")

saludar("Juan")

Renzo Valentin - 2025