@abstractmethod-2

 

Clases Abstractas en Python: La Fábrica de Juguetes 🏭🧸

Imagina que tienes una fábrica de juguetes. No produces juguetes genéricos, sino tipos específicos: carros, muñecas y rompecabezas.

La idea principal

Una clase abstracta es como el plano general de la fábrica:

python
from abc import ABC, abstractmethod

class Juguete(ABC):  # Este es nuestro "plano general"
    @abstractmethod
    def hacer_sonido(self):
        pass  # ¡No sabemos qué sonido hará cada juguete!
    
    @abstractmethod
    def mover(self):
        pass  # ¡No sabemos cómo se moverá cada juguete!

¿Por qué usar clases abstractas?

❌ NO PUEDES crear un "juguete genérico"

python
# Esto NO funciona - da error!
juguete_generico = Juguete()  # TypeError!

✅ SÍ PUEDES crear juguetes específicos

python
class Carro(Juguete):
    def hacer_sonido(self):
        return "¡Run run! 🚗"
    
    def mover(self):
        return "Rodando por el piso"

class Munieca(Juguete):
    def hacer_sonido(self):
        return "¡Hola! 👋"
    
    def mover(self):
        return "Caminando elegantemente"

Ejemplo completo: Nuestra fábrica en acción

python
from abc import ABC, abstractmethod

# El plano maestro (clase abstracta)
class Juguete(ABC):
    @abstractmethod
    def hacer_sonido(self):
        pass
    
    @abstractmethod
    def mover(self):
        pass
    
    # Método concreto - TODOS los juguetes lo heredan
    def presentarse(self):
        return f"Soy un juguete y {self.mover()}"

# Juguetes específicos
class Carro(Juguete):
    def hacer_sonido(self):
        return "¡Run run! 🚗"
    
    def mover(self):
        return "rodando por el piso"

class Munieca(Juguete):
    def hacer_sonido(self):
        return "¡Hola! 👋"
    
    def mover(self):
        return "caminando elegantemente"

# ¡A jugar!
carro_rojo = Carro()
munieca_lucia = Munieca()

print(carro_rojo.hacer_sonido())  # ¡Run run! 🚗
print(munieca_lucia.presentarse())  # Soy un juguete y caminando elegantemente

Analogía del restaurante 🍽️

Clase abstracta = Receta base de "pizza"

  • Obligatorio: tener masa, queso, ingredientes

  • Opcional: método para hornear (ya está implementado)

Clases concretas = Pizzas específicas

  • PizzaMargherita, PizzaPepperoni, PizzaHawaiana

  • Cada una define SUS ingredientes específicos

¿Cuándo usar clases abstractas?

  1. Cuando quieres un contrato: "Todos los que hereden de mí DEBEN implementar estos métodos"

  2. Para organizar código: Agrupar funcionalidad común

  3. Para evitar errores: Python te avisa si olvidas implementar un método obligatorio

Resumen visual

text
JUGUETE (Abstracta) ← No se puede instanciar
├── debe tener: hacer_sonido() ❗
├── debe tener: mover() ❗
└── ya tiene: presentarse() ✅
    │
    ├── CARRO (Concreta)
    │   ├── hacer_sonido() = "¡Run run!"
    │   └── mover() = "Rodando"
    │
    └── MUÑECA (Concreta)
        ├── hacer_sonido() = "¡Hola!"
        └── mover() = "Caminando"

¡Así garantizas que todos tus juguetes se comporten como juguetes! 🎯

explicame @classmethod y con una analogia

@classmethod en Python: El Director de la Fábrica 🎬🏭

Imagina que tienes una fábrica de pizzas donde el director (classmethod) puede tomar decisiones sobre cómo crear pizzas sin necesidad de que exista una pizza específica.

La idea principal

Un @classmethod es un método que trabaja con la clase misma, no con instancias específicas.

python
class Pizza:
    def __init__(self, ingredientes):
        self.ingredientes = ingredientes
    
    # Método normal - trabaja con UNA pizza específica
    def describir(self):
        return f"Pizza con: {', '.join(self.ingredientes)}"
    
    # Classmethod - trabaja con la CLASE Pizza
    @classmethod
    def margherita(cls):
        return cls(["salsa tomate", "mozzarella", "albahaca"])
    
    @classmethod
    def pepperoni(cls):
        return cls(["salsa tomate", "mozzarella", "pepperoni"])

Analogía del Restaurante de Pizzas 🍕

El Chef vs El Director

  • Métodos normales = Chef individual (trabaja con una pizza específica)

  • @classmethod = Director del restaurante (crea recetas estándar)

python
class Pizza:
    def __init__(self, nombre, ingredientes):
        self.nombre = nombre
        self.ingredientes = ingredientes
    
    # Método normal - el chef trabajando
    def cocinar(self):
        return f"Cocinando {self.nombre} 🍕"
    
    # Classmethod - el director creando recetas estándar
    @classmethod
    def crear_margherita(cls):
        return cls("Margherita", ["salsa", "mozzarella", "albahaca"])
    
    @classmethod
    def crear_cuatro_quesos(cls):
        return cls("Cuatro Quesos", ["mozzarella", "gorgonzola", "parmesano", "fontina"])
    
    @classmethod
    def crear_personalizada(cls, nombre, *ingredientes):
        return cls(nombre, list(ingredientes))

# ¡A trabajar!

Diferencia clave: cls vs self

python
class FabricaJuguetes:
    material = "plástico"  # Atributo de CLASE
    
    def __init__(self, color):
        self.color = color  # Atributo de INSTANCIA
    
    # Método normal - usa self
    def pintar(self, nuevo_color):
        self.color = nuevo_color  # Cambia UNA instancia
    
    # Classmethod - usa cls
    @classmethod
    def cambiar_material_fabrica(cls, nuevo_material):
        cls.material = nuevo_material  # Cambia TODAS las instancias

Ejemplos prácticos en acción

Ejemplo 1: Fábrica de Empleados

python
class Empleado:
    empresa = "TechCorp"
    
    def __init__(self, nombre, salario):
        self.nombre = nombre
        self.salario = salario
    
    # Classmethod para crear empleados estándar
    @classmethod
    def crear_gerente(cls, nombre):
        return cls(nombre, salario=50000)
    
    @classmethod
    def crear_desarrollador(cls, nombre):
        return cls(nombre, salario=40000)
    
    @classmethod
    def cambiar_empresa(cls, nueva_empresa):
        cls.empresa = nueva_empresa

# Uso
gerente = Empleado.crear_gerente("Ana")  # No necesita instancia existente
dev = Empleado.crear_desarrollador("Luis")

print(gerente.salario)  # 50000
print(dev.salario)      # 40000

# Cambio que afecta a TODOS
Empleado.cambiar_empresa("MetaTech")
print(gerente.empresa)  # MetaTech
print(dev.empresa)      # MetaTech

Ejemplo 2: Sistema de Conversiones

python
class Temperatura:
    def __init__(self, celsius):
        self.celsius = celsius
    
    @classmethod
    def desde_fahrenheit(cls, fahrenheit):
        celsius = (fahrenheit - 32) * 5/9
        return cls(celsius)
    
    @classmethod
    def desde_kelvin(cls, kelvin):
        celsius = kelvin - 273.15
        return cls(celsius)

# Diferentes formas de crear temperaturas
t1 = Temperatura(25)  # 25°C directamente
t2 = Temperatura.desde_fahrenheit(77)  # 25°C desde Fahrenheit
t3 = Temperatura.desde_kelvin(298.15)  # 25°C desde Kelvin

print(f"Todas son 25°C: {t1.celsius}, {t2.celsius}, {t3.celsius}")

¿Cuándo usar @classmethod?

✅ SÍ usar classmethod cuando:

  1. Métodos de fábrica: Crear instancias de diferentes maneras

  2. Manipular atributos de clase: Cambios que afecten a TODAS las instancias

  3. Métodos alternativos de construcción: Como desde_json()desde_csv()

❌ NO usar classmethod cuando:

  1. Necesitas trabajar con datos de una instancia específica

  2. El método no necesita acceso a la clase

Resumen visual

text
FÁBRICA DE PIZZAS 🏭

DIRECTOR (@classmethod)
├── crear_margherita() → Pizza Margherita
├── crear_pepperoni() → Pizza Pepperoni  
└── cambiar_receta() → Afecta a TODAS

CHEF (método normal)
├── cocinar() → Esta pizza específica
├── cortar() → Esta pizza específica
└── servir() → Esta pizza específica

Ventajas clave

  1. Flexibilidad: Múltiples formas de crear objetos

  2. Organización: Lógica de creación agrupada en la clase

  3. Coherencia: Métodos que trabajan a nivel de clase, no de instancia

¡Así el director (classmethod) puede crear "recetas estándar" sin ensuciarse las manos con pizzas individuales!

Comentarios

Entradas populares de este blog

¿Qué es un Closure?

4 tipos de colecciones de datos más

Funciones en Python: con y sin paréntesis