Decoradores para Principiantes

Analogía: El Asistente Personal

Imagina que tienes un chef (tu función) que cocina platos. Un decorador sería como un asistente personal que:

  1. Prepara la cocina antes de que el chef empiece

  2. Limpia todo después de que el chef termina

  3. No modifica cómo el chef cocina

El asistente envuelve/al chef y añade funcionalidad extra sin cambiar su trabajo principal.


Ejemplo 1: Decorador "Saludador" 🎉

python
# El decorador (asistente personal)
def decorador_saludar(funcion_original):
    def funcion_envoltura():
        print("🎉 ¡La función va a empezar!")  # Esto pasa ANTES
        funcion_original()  # Aquí se ejecuta la función original
        print("🎊 ¡La función terminó!")  # Esto pasa DESPUÉS
    return funcion_envoltura

# La función original (el chef)
def mi_funcion():
    print("👨‍🍳 Estoy cocinando/computando...")

# Aplicar el decorador manualmente
mi_funcion_decorada = decorador_saludar(mi_funcion)

print("=== SIN DECORADOR ===")
mi_funcion()

print("\n=== CON DECORADOR ===")
mi_funcion_decorada()

Output:

text
=== SIN DECORADOR ===
👨‍🍳 Estoy cocinando/computando...

=== CON DECORADOR ===
🎉 ¡La función va a empezar!
👨‍🍳 Estoy cocinando/computando...
🎊 ¡La función terminó!

Ahora con la sintaxis @ (más limpia):

python
def decorador_saludar(funcion_original):
    def funcion_envoltura():
        print("🎉 ¡La función va a empezar!")
        funcion_original()
        print("🎊 ¡La función terminó!")
    return funcion_envoltura

# Usando @ es equivalente a: mi_funcion = decorador_saludar(mi_funcion)
@decorador_saludar
def mi_funcion():
    print("👨‍🍳 Estoy cocinando/computando...")

# Simplemente llamamos la función
mi_funcion()

Ejemplo 2: Decorador "Contador de Llamadas" 🔢

python
def contador_llamadas(funcion_original):
    # Variable que recuerda cuántas veces se llamó
    contador = 0
    
    def funcion_envoltura():
        # Usamos nonlocal para modificar la variable del scope exterior
        nonlocal contador
        contador += 1
        print(f"📞 Esta es la llamada número: {contador}")
        return funcion_original()
    
    return funcion_envoltura

@contador_llamadas
def saludar():
    print("¡Hola mundo!")

# Probemos llamar la función varias veces
saludar()
saludar()
saludar()

Output:

text
📞 Esta es la llamada número: 1
¡Hola mundo!
📞 Esta es la llamada número: 2
¡Hola mundo!
📞 Esta es la llamada número: 3
¡Hola mundo!

Ejemplo 3: Decorador "Verificador de Edad" 🎂

python
def verificar_edad(funcion_original):
    def funcion_envoltura(edad):
        if edad < 18:
            print("❌ Acceso denegado. Eres menor de edad.")
            return
        else:
            print("✅ Acceso permitido. Eres mayor de edad.")
            return funcion_original(edad)
    return funcion_envoltura

@verificar_edad
def entrar_al_bar(edad):
    print("🍻 ¡Bienvenido al bar!")

# Probemos con diferentes edades
print("=== EDAD 16 ===")
entrar_al_bar(16)

print("\n=== EDAD 25 ===")
entrar_al_bar(25)

Output:

text
=== EDAD 16 ===
❌ Acceso denegado. Eres menor de edad.

=== EDAD 25 ===
✅ Acceso permitido. Eres mayor de edad.
🍻 ¡Bienvenido al bar!

¿Qué está pasando realmente?

Sin decorador:

python
def mi_funcion():
    print("Hola")

# Llamada normal
mi_funcion()  # Output: "Hola"

Con decorador:

python
def decorador(func):
    def wrapper():
        print("Antes")
        func()
        print("Después")
    return wrapper

@decorador
def mi_funcion():
    print("Hola")

# Esto es equivalente a:
# mi_funcion = decorador(mi_funcion)

mi_funcion()
# Output:
# "Antes"
# "Hola"
# "Después"

Resumen Visual

text
FUNCIÓN ORIGINAL: [ print("Hola") ]

CON DECORADOR:

[ DECORADOR ] → [ print("Antes") ] → [ print("Hola") ] → [ print("Después") ]
     ↑
Envuelve la función original

Puntos Clave para Principiantes:

  1. Un decorador es una función que toma otra función y devuelve una nueva función

  2. Extiende comportamiento sin modificar la función original

  3. La sintaxis @ es solo azúcar sintáctica para hacerlo más legible

  4. Piensa en ello como: "antes y después" de la función original

Comentarios

Entradas populares de este blog

¿Qué es un Closure?

Calculadora de edad

Funciones en Python: con y sin paréntesis