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

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

    ftplib en Python: FTP y FTPS seguros

    Aprende ftplib en Python para listar, descargar y subir archivos por FTP o FTPS con TLS, timeouts, límites, reanudación y

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Rack de servidores que representa un endpoint creado con xmlrpc.server en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.server: crea servidores XML-RPC

    Aprende xmlrpc.server en Python para crear servidores XML-RPC, registrar funciones, limitar métodos y rutas y evitar exposición insegura.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Código de error sobre datos binarios que representa fallos manejados con urllib.error en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    urllib.error en Python: maneja errores HTTP

    Aprende urllib.error en Python para manejar URLError, HTTPError, descargas incompletas, retries selectivos y diagnósticos de red claros.

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Teclas con la palabra HTML que representan entidades HTML en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.entities: convierte entidades HTML

    Aprende html.entities en Python para consultar entidades HTML, convertir nombres y code points y no confundir decodificación con sanitización.

    Ler mais

    Tempo de leitura: 7 minutos
    21/08/2026
    Carpeta con archivos que representa tipos MIME identificados con mimetypes en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes: detecta tipos MIME de archivos

    Aprende mimetypes en Python para identificar tipos de archivo, validar cargas y definir Content-Type con más seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    20/08/2026
    Código HTML en una pantalla que representa análisis con html.parser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.parser en Python: analiza HTML

    Aprende html.parser en Python para extraer texto, enlaces y metadatos, procesar HTML por bloques y no confundir parsing con sanitización.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026