Decoradores Personalizados en Clases-1

 

Decoradores Personalizados en Clases - Ejemplo Sencillo

Concepto básico:

Un decorador personalizado es una función que modifica el comportamiento de otra función o clase.

Ejemplo 1: Decorador simple para funciones

python
def mi_decorador(funcion):
    def wrapper():
        print("Algo pasa ANTES de la función")
        funcion()
        print("Algo pasa DESPUÉS de la función")
    return wrapper

@mi_decorador
def saludar():
    print("¡Hola!")

saludar()
# Output:
# Algo pasa ANTES de la función
# ¡Hola!
# Algo pasa DESPUÉS de la función

Ejemplo 2: Decorador para métodos de clase

python
def logear_metodo(funcion):
    def wrapper(self, *args, **kwargs):
        print(f"📝 Ejecutando: {funcion.__name__}")
        resultado = funcion(self, *args, **kwargs)
        print(f"✅ Completado: {funcion.__name__}")
        return resultado
    return wrapper

class Calculadora:
    @logear_metodo
    def sumar(self, a, b):
        return a + b
    
    @logear_metodo
    def restar(self, a, b):
        return a - b

calc = Calculadora()
calc.sumar(5, 3)
# Output:
# 📝 Ejecutando: sumar
# ✅ Completado: sumar

calc.restar(10, 4)
# Output:
# 📝 Ejecutando: restar
# ✅ Completado: restar

Ejemplo 3: Decorador con parámetros

python
def repetir(veces):
    def decorador(funcion):
        def wrapper(*args, **kwargs):
            for i in range(veces):
                print(f"Ejecución {i+1}:")
                funcion(*args, **kwargs)
        return wrapper
    return decorador

class Mensajero:
    @repetir(veces=3)
    def enviar_mensaje(self, mensaje):
        print(f"Enviando: {mensaje}")

msg = Mensajero()
msg.enviar_mensaje("¡Hola Mundo!")
# Output:
# Ejecución 1:
# Enviando: ¡Hola Mundo!
# Ejecución 2:
# Enviando: ¡Hola Mundo!
# Ejecución 3:
# Enviando: ¡Hola Mundo!

Ejemplo 4: Decorador para validar datos

python
def validar_edad(funcion):
    def wrapper(self, edad):
        if edad < 0:
            print("❌ Edad no puede ser negativa")
            return
        if edad > 120:
            print("❌ Edad no puede ser mayor a 120")
            return
        return funcion(self, edad)
    return wrapper

class Persona:
    def __init__(self, nombre):
        self.nombre = nombre
    
    @validar_edad
    def establecer_edad(self, edad):
        self.edad = edad
        print(f"✅ {self.nombre} tiene {self.edad} años")

persona = Persona("Ana")
persona.establecer_edad(25)  # ✅ Ana tiene 25 años
persona.establecer_edad(-5)  # ❌ Edad no puede ser negativa
persona.establecer_edad(150) # ❌ Edad no puede ser mayor a 120

Ejemplo 5: Decorador que mide tiempo

python
import time

def medir_tiempo(funcion):
    def wrapper(*args, **kwargs):
        inicio = time.time()
        resultado = funcion(*args, **kwargs)
        fin = time.time()
        print(f"⏰ {funcion.__name__} tardó {fin-inicio:.2f} segundos")
        return resultado
    return wrapper

class Procesador:
    @medir_tiempo
    def procesar_lento(self):
        time.sleep(1)  # Simula proceso lento
        return "Procesado"

proc = Procesador()
proc.procesar_lento()
# Output: ⏰ procesar_lento tardó 1.00 segundos

En resumen:

Un decorador personalizado es como agregar superpoderes a tus funciones/métodos:

python
@superpoder          # ← Así se usa
def mi_funcion():     # ← Tu función normal
    pass

¿Ves lo simple que es? ¡Son como "envoltorios mágicos" para tus funciones


Decoradores Personalizados: Los Asistentes Mágicos del Restaurante 🎩🍽️

Imagina que tienes un restaurante de lujo donde cada chef (método) puede tener asistentes especializados (decoradores) que le ayudan sin cambiar su forma de cocinar.

El Restaurante Mágico

python
# NUESTROS ASISTENTES MÁGICOS (decoradores)
def asistente_verificador(func):
    """El asistente que verifica los ingredientes"""
    def asistente(*args, **kwargs):
        print("🔍 ASISTENTE: Verificando que los ingredientes estén frescos...")
        resultado = func(*args, **kwargs)
        print("✅ ASISTENTE: Ingredientes aprobados!")
        return resultado
    return asistente

def asistente_tiempo(func):
    """El asistente que controla el tiempo de cocción"""
    def asistente(*args, **kwargs):
        print("⏱️  ASISTENTE: Iniciando timer...")
        resultado = func(*args, **kwargs)
        print("⏱️  ASISTENTE: Tiempo perfecto!")
        return resultado
    return asistente

def asistente_presentacion(func):
    """El asistente que decora el plato"""
    def asistente(*args, **kwargs):
        resultado = func(*args, **kwargs)
        plato_decorado = f"🎨 {resultado} con decoración especial"
        return plato_decorado
    return asistente

Los Chefs y Sus Asistentes

python
class Restaurante:
    def __init__(self, nombre_chef):
        self.nombre_chef = nombre_chef
    
    @asistente_verificador
    @asistente_tiempo
    def preparar_ensalada(self):
        print(f"👨‍🍳 {self.nombre_chef}: Preparando ensalada...")
        return "Ensalada César"
    
    @asistente_presentacion
    @asistente_verificador
    def preparar_postre(self):
        print(f"👨‍🍳 {self.nombre_chef}: Preparando postre...")
        return "Tiramisú"

# Vamos al restaurante
restaurante = Restaurante("Chef Antonio")

print("=== PREPARAR ENSALADA ===")
plato1 = restaurante.preparar_ensalada()
print(f"Plato listo: {plato1}")

print("\n=== PREPARAR POSTRE ===")
plato2 = restaurante.preparar_postre()
print(f"Plato listo: {plato2}")

Salida:

text
=== PREPARAR ENSALADA ===
🔍 ASISTENTE: Verificando que los ingredientes estén frescos...
⏱️  ASISTENTE: Iniciando timer...
👨‍🍳 Chef Antonio: Preparando ensalada...
⏱️  ASISTENTE: Tiempo perfecto!
✅ ASISTENTE: Ingredientes aprobados!
Plato listo: Ensalada César

=== PREPARAR POSTRE ===
🔍 ASISTENTE: Verificando que los ingredientes estén frescos...
👨‍🍳 Chef Antonio: Preparando postre...
Plato listo: 🎨 Tiramisú con decoración especial

Asistentes con Superpoderes Especiales

python
def asistente_repetir(veces):
    """El asistente que repite la receta varias veces"""
    def asistente_real(func):
        def ayudante(*args, **kwargs):
            resultados = []
            for i in range(veces):
                print(f"🔄 ASISTENTE: Preparando lote {i+1} de {veces}")
                resultado = func(*args, **kwargs)
                resultados.append(resultado)
            return resultados
        return ayudante
    return asistente_real

def asistente_calidad(nivel):
    """El asistente que controla la calidad según el nivel"""
    def asistente_real(func):
        def ayudante(*args, **kwargs):
            if nivel == "alto":
                print("⭐ ASISTENTE: Modo CALIDAD PREMIUM activado")
            elif nivel == "medio":
                print("👍 ASISTENTE: Modo CALIDAD ESTÁNDAR activado")
            
            resultado = func(*args, **kwargs)
            
            if nivel == "alto":
                resultado += " ⭐ (CALIDAD PREMIUM)"
            return resultado
        return ayudante
    return asistente_real

class Panaderia:
    def __init__(self, nombre_panadero):
        self.nombre_panadero = nombre_panadero
    
    @asistente_repetir(3)
    def hacer_pan(self):
        print(f"🥖 {self.nombre_panadero}: Amasando pan...")
        return "Pan recién horneado"
    
    @asistente_calidad("alto")
    def hacer_pastel(self):
        print(f"🎂 {self.nombre_panadero}: Decorando pastel...")
        return "Pastel de chocolate"

# Vamos a la panadería
panaderia = Panaderia("Panadero Carlos")

print("=== HACER PAN (3 LOTES) ===")
panes = panaderia.hacer_pan()
print(f"Resultado: {panes}")

print("\n=== HACER PASTEL (CALIDAD ALTA) ===")
pastel = panaderia.hacer_pastel()
print(f"Resultado: {pastel}")

El Gran Restaurante con Todos los Asistentes

python
class GranRestaurante:
    def __init__(self, nombre):
        self.nombre = nombre
        self.platos_preparados = 0
    
    # Asistente personalizado para contar platos
    def contar_platos(func):
        def asistente(self, *args, **kwargs):
            self.platos_preparados += 1
            resultado = func(self, *args, **kwargs)
            print(f"📊 ASISTENTE: Plato número {self.platos_preparados} preparado!")
            return resultado
        return asistente
    
    @asistente_verificador
    @asistente_tiempo
    @contar_platos
    def plato_especial(self, nombre_plato):
        print(f"👨‍🍳 {self.nombre}: Creando {nombre_plato}...")
        return f"🎉 {nombre_plato} ESPECIAL"
    
    @asistente_presentacion
    @contar_platos
    def plato_rapido(self, nombre_plato):
        print(f"👨‍🍳 {self.nombre}: Preparando rápido {nombre_plato}...")
        return nombre_plato

# El restaurante en acción
restaurante_lujo = GranRestaurante("Chef Master")

print("=== PLATO ESPECIAL ===")
plato1 = restaurante_lujo.plato_especial("Salmón a la parrilla")
print(f"Resultado: {plato1}")

print("\n=== PLATO RÁPIDO ===")
plato2 = restaurante_lujo.plato_rapido("Sopa del día")
print(f"Resultado: {plato2}")

print(f"\n📈 Total platos preparados: {restaurante_lujo.platos_preparados}")

Analogía Completa:

text
RESTAURANTE DE LUJO (Tu Clase)

CHEF PRINCIPAL (Métodos normales)
├── preparar_plato() - Sabe cocinar
└── Su trabajo principal es crear platos

ASISTENTES MÁGICOS (Decoradores)
├── 🕵️‍♂️ ASISTENTE VERIFICADOR → Revisa ingredientes
├── ⏱️  ASISTENTE TIEMPO → Controla cocción  
├── 🎨 ASISTENTE PRESENTACIÓN → Decora el plato
├── 🔄 ASISTENTE REPETIR → Prepara múltiples porciones
└── ⭐ ASISTENTE CALIDAD → Controla nivel de calidad

CADA PLATO puede tener DIFERENTES ASISTENTES

¿Por qué esto es mágico? ✨

  1. El chef no se distrae: Solo se concentra en cocinar

  2. Los asistentes son reutilizables: Un mismo asistente ayuda a muchos chefs

  3. Puedes mezclar y combinar: Diferentes combinaciones para diferentes platos

  4. Fácil de mantener: Si cambia un asistente, afecta a todos automáticamente

Ejemplo del Mundo Real:

python
# Asistentes para un sistema bancario
def verificar_sesion(func):
    def asistente(self, *args, **kwargs):
        if not self.sesion_activa:
            return "❌ Por favor inicia sesión primero"
        return func(self, *args, **kwargs)
    return asistente

def registrar_transaccion(func):
    def asistente(self, *args, **kwargs):
        resultado = func(self, *args, **kwargs)
        print(f"📝 Transacción registrada: {func.__name__}")
        return resultado
    return asistente

class Banco:
    def __init__(self):
        self.sesion_activa = False
    
    def login(self):
        self.sesion_activa = True
        return "✅ Sesión iniciada"
    
    @verificar_sesion
    @registrar_transaccion
    def retirar_dinero(self, cantidad):
        return f"💰 Retiraste ${cantidad}"

¡Los decoradores son como tener un equipo de asistentes mágicos que hacen que tu código sea más poderoso sin esfuerzo

Comentarios

Entradas populares de este blog

¿Qué es un Closure?

Calculadora de edad

Funciones en Python: con y sin paréntesis