Herencia múltiple en Python: MRO, super() y mixins

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Herança múltipla em Python sem causar problemas no código

La herencia múltiple permite que una clase de Python reciba comportamiento de más de una clase base. Es una función poderosa, pero también puede crear jerarquías difíciles de entender cuando se utiliza sin una estructura clara. El objetivo no es acumular padres, sino combinar responsabilidades pequeñas sin duplicar código.

Antes de profundizar, revisa las guías sobre programación orientada a objetos, uso de super(), decoradores, dataclasses y funciones. Como referencias externas, consulta el tutorial oficial de clases y la documentación oficial de super().

Qué es la herencia múltiple

En una herencia simple, una clase deriva de un único padre. En la herencia múltiple se indican varias bases entre paréntesis:

class Volador:
    def volar(self):
        return "Volando"

class Nadador:
    def nadar(self):
        return "Nadando"

class Pato(Volador, Nadador):
    pass

pato = Pato()
print(pato.volar())
print(pato.nadar())

Pato combina dos capacidades independientes. Este ejemplo funciona bien porque las clases base no compiten por el mismo método ni administran estados incompatibles.

El problema de métodos repetidos

Cuando dos padres definen un método con el mismo nombre, Python necesita decidir cuál utilizar:

class A:
    def describir(self):
        return "Clase A"

class B:
    def describir(self):
        return "Clase B"

class C(A, B):
    pass

print(C().describir())  # Clase A

La clase situada primero tiene prioridad en este caso. Sin embargo, la decisión real sigue el orden de resolución de métodos, conocido como MRO.

Comprender el MRO

El MRO define el orden en que Python busca atributos y métodos. Puedes inspeccionarlo con mro():

print(C.mro())

El resultado incluye C, después A, B y finalmente object. Python utiliza una linearización consistente para evitar recorridos contradictorios. No conviene memorizar el algoritmo completo; lo importante es revisar el MRO cuando una jerarquía tenga más de dos niveles.

El problema del diamante

El llamado problema del diamante aparece cuando dos clases heredan de una base común y una cuarta clase hereda de ambas:

class Base:
    def procesar(self):
        print("Base")

class Izquierda(Base):
    def procesar(self):
        print("Izquierda")
        super().procesar()

class Derecha(Base):
    def procesar(self):
        print("Derecha")
        super().procesar()

class Final(Izquierda, Derecha):
    def procesar(self):
        print("Final")
        super().procesar()

Final().procesar()

La salida recorre cada implementación una sola vez porque todas cooperan con super(). Llamar directamente a Base.procesar(self) desde varios padres podría ejecutar la base más de una vez.

super() no significa simplemente “el padre”

En herencia múltiple, super() significa “continúa con la siguiente clase del MRO”. Por eso cada clase cooperativa debe llamar a super() y aceptar una interfaz compatible.

class Identificado:
    def __init__(self, *, identificador, **kwargs):
        self.identificador = identificador
        super().__init__(**kwargs)

class Registrado:
    def __init__(self, *, usuario, **kwargs):
        self.usuario = usuario
        super().__init__(**kwargs)

class Recurso(Identificado, Registrado):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

recurso = Recurso(identificador=10, usuario="ana")

Los argumentos nombrados y **kwargs permiten que cada clase consuma únicamente los datos que le pertenecen y envíe el resto a la siguiente.

Mixins

Un mixin es una clase pequeña que añade una capacidad específica, pero no pretende representar una entidad completa. Normalmente no controla el ciclo de vida principal del objeto.

import json

class JsonMixin:
    def to_json(self):
        return json.dumps(self.__dict__, ensure_ascii=False)

class LogMixin:
    def registrar(self, mensaje):
        print(f"[{self.__class__.__name__}] {mensaje}")

class Usuario(JsonMixin, LogMixin):
    def __init__(self, nombre):
        self.nombre = nombre

Los nombres terminados en Mixin ayudan a comunicar que esa clase ofrece comportamiento reutilizable.

Cuándo usar composición

La composición suele ser mejor cuando la relación es “tiene un” en lugar de “es un”. Un servicio de informes puede tener un exportador y un repositorio, sin heredar de ellos:

class ServicioInformes:
    def __init__(self, repositorio, exportador):
        self.repositorio = repositorio
        self.exportador = exportador

La composición reduce el acoplamiento y permite sustituir dependencias con facilidad durante las pruebas.

Diseñar interfaces compatibles

Las clases que participan en una cadena cooperativa deben usar nombres de parámetros compatibles y evitar asumir que son las últimas. También deben llamar a super() incluso cuando parezca innecesario, porque otra clase puede incorporarse después.

Evitar estados duplicados

No permitas que varios padres creen atributos con el mismo nombre para significados distintos. Un atributo estado utilizado por dos clases puede producir errores difíciles de rastrear. Prefiere nombres explícitos como estado_conexion y estado_documento.

Pruebas del MRO

Cuando el orden sea importante, escríbelo como una expectativa:

def test_mro_recurso():
    nombres = [clase.__name__ for clase in Recurso.mro()]
    assert nombres[:3] == ["Recurso", "Identificado", "Registrado"]

También prueba que cada inicializador se ejecute una sola vez y que todos los atributos esperados existan.

Errores frecuentes

Los problemas habituales son llamar directamente a un padre, mezclar clases cooperativas con clases que no llaman a super(), repetir nombres de atributos, crear demasiados niveles y utilizar mixins que contienen demasiada lógica. Otro error es cambiar el orden de las bases sin revisar cómo cambia el MRO.

Reglas prácticas

Mantén las clases base pequeñas, usa mixins para capacidades concretas, inspecciona mro(), utiliza argumentos nombrados en inicializadores cooperativos y documenta el contrato de cada clase. Cuando la jerarquía resulte difícil de explicar en pocas frases, probablemente convenga reemplazar parte de ella por composición.

Conclusión

La herencia múltiple funciona bien cuando combina responsabilidades independientes y todas las clases respetan el MRO. super() debe utilizarse como una cadena cooperativa, no como una llamada rígida a un padre específico. Los mixins pequeños, las interfaces compatibles y las pruebas del orden de resolución permiten aprovechar esta característica sin crear jerarquías frágiles.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ipaddress en Python: redes IPv4 e IPv6

    Aprende ipaddress en Python para validar IPv4 e IPv6, calcular redes CIDR, dividir subredes y crear políticas de acceso más

    Ler mais

    Tempo de leitura: 5 minutos
    18/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    queue en Python: coordina hilos

    Aprende queue en Python para coordinar hilos con FIFO, prioridad, backpressure, tracking, reintentos y shutdown seguro.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Chic portrait of a woman wearing trendy sunglasses reflecting numbers, captured in a modern setting.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    struct en Python: datos binarios

    Aprende struct en Python para empaquetar datos binarios, controlar endianness, reutilizar buffers y validar protocolos externos.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile en Python: crea TAR seguro

    Aprende tarfile en Python para crear TAR comprimido, inspeccionar miembros y extraer con filtros, límites y protección de rutas.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Row of colorful office binders neatly arranged on a shelf, ideal for organization concepts.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    gzip en Python: comprime archivos .gz

    Aprende gzip en Python para leer y escribir .gz, crear streams reproducibles, procesar datos grandes y limitar la expansión externa.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Neatly arranged blue office binders labeled with dates and names for organized storage.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    lzma en Python: comprime archivos XZ

    Aprende lzma en Python para crear archivos XZ, procesar streams, elegir checks y filtros y limitar memoria con datos externos.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026