Clases en Python — Parte III 🌌

Herencia múltiple, ABC, polimorfismo, duck typing, extensión de built-ins y dataclasses

🌌 Consejo de los Sabios

Cerramos con herencia multinivel y múltiple, un ejemplo “bueno” de herencia, clases abstractas, polimorfismo y duck typing, cómo extender tipos incorporados, y dataclasses para datos inmutables o simples.

15) Herencia multinivel y 16) múltiple

class SerVivo:
    def info(self): return "ser vivo"

class Mamifero(SerVivo):          # multinivel
    def info(self): return super().info() + " → mamífero"

class Volador:
    def vuela(self): return "vuela"

class Murcielago(Mamifero, Volador):   # múltiple
    pass

m = Murcielago()
print(m.info(), m.vuela())

Nota: Orden de resolución de métodos (MRO) gobierna cómo se buscan atributos en herencia múltiple.

17) Buen ejemplo de herencia

class Repositorio:
    def guardar(self, entidad): raise NotImplementedError

class RepoEnMemoria(Repositorio):
    def __init__(self): self._data = []
    def guardar(self, entidad): self._data.append(entidad)

class RepoArchivo(Repositorio):
    def __init__(self, ruta): self._ruta = ruta
    def guardar(self, entidad): open(self._ruta, "a").write(str(entidad)+"\n")

Criterio: heredar cuando hay una relación “es-un” clara y se comparte contrato (interfaz).

18) Clases abstractas (abc.ABC)

from abc import ABC, abstractmethod

class Figura(ABC):
    @abstractmethod
    def area(self): ...

class Rectangulo(Figura):
    def __init__(self, a, b): self.a=a; self.b=b
    def area(self): return self.a * self.b

Beneficio: asegurar que las subclases implementen métodos requeridos.

19) Polimorfismo y 20) Duck typing

def imprimir_area(figura):
    # Duck typing: “si tiene area(), sirve”
    print("Área:", figura.area())

class Circulo:
    def __init__(self, r): self.r = r
    def area(self): return 3.14159 * self.r * self.r

imprimir_area(Rectangulo(2,3))
imprimir_area(Circulo(1.5))

Clave en Python: el tipo estructural (métodos disponibles) prima sobre la jerarquía formal.

21) Extender tipos incorporados

class ListaConSuma(list):
    def suma(self):
        return sum(self)

nums = ListaConSuma([1,2,3])
print(nums.suma())

Advertencia: heredar de built-ins es conveniente pero considera composición si necesitas mayor control.

22) Data classes (@dataclass)

from dataclasses import dataclass

@dataclass(frozen=True)  # inmutable; genera __init__, __repr__, __eq__
class Punto:
    x: float
    y: float

p = Punto(1.0, 2.0)
print(p)  # Punto(x=1.0, y=2.0)

Ventaja: menos código ceremonial para objetos de datos; frozen=True facilita inmutabilidad.

💭 Checkpoint (reflexión)

  1. ¿Qué problema resuelve una clase abstracta frente a solo documentar una interfaz?
  2. Da un ejemplo donde duck typing sea preferible a herencia.
  3. ¿Cuándo preferirías una dataclass inmutable (frozen=True)?