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

Actualizado el: 20/08/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

    Código Python en pantalla representando EnumDict y enumeraciones avanzadas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    EnumDict: personaliza Enums con validación

    Aprende EnumDict en Python para personalizar EnumType, validar miembros y crear enumeraciones avanzadas con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    11/10/2026
    Rack de servidores que representa el balanceo de conexiones con SO_REUSEPORT_LB en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: reparte conexiones entre workers

    Aprende SO_REUSEPORT_LB en Python para distribuir conexiones entre workers con pruebas, portabilidad y cierre ordenado.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026
    Código Python asíncrono en un portátil para inspect.markcoroutinefunction
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: detecta wrappers async

    Aprende inspect.markcoroutinefunction en Python para identificar wrappers asíncronos, integrar frameworks y evitar detecciones incorrectas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Código Python para recorrer carpetas y archivos con Path.walk
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: recorre directorios con seguridad

    Aprende Path.walk en Python para recorrer directorios, filtrar archivos, tratar errores y controlar la travesía con seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Depuración de un proceso Python en terminal con código
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depura procesos Python en ejecución

    Aprende a conectar pdb a un proceso Python en ejecución, inspeccionar la pila y diagnosticar bloqueos de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python para representar fracciones exactas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: convierte números en fracciones

    Aprende fractions.from_number en Python para convertir números en fracciones exactas, controlar precisión, validar entradas y evitar redondeos inesperados.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026