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

    Criando instalador EXE com ícone personalizado em Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un ejecutable EXE con icono personalizado en Python

    Crea un EXE de Python con PyInstaller, icono ICO, onefile, windowed, archivos de datos, SPEC, rutas seguras, pruebas y distribución.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Uso do super em Python para resolver problemas de herança
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Cómo usar super() en Python sin errores de herencia

    Aprende a usar super() en Python con __init__, MRO, herencia múltiple, mixins, argumentos cooperativos, pruebas y composición.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Uso de multiprocessing em Python para acelerar scripts
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Multiprocessing en Python: usa varios núcleos correctamente

    Aprende multiprocessing en Python con Process, Pool, Queue, memoria compartida, ProcessPoolExecutor, benchmarks y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Criação de geradores eficientes usando yield em Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    yield en Python: crea generadores eficientes

    Aprende yield en Python para crear generadores, procesar archivos grandes, construir pipelines, usar yield from y reducir el consumo de

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Decoradores en Python: guía práctica y ejemplos

    Aprende decoradores en Python con wrappers, argumentos, functools.wraps, parámetros, validación, rendimiento y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Caixas empilhadas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Módulos y paquetes en Python: guía completa

    Aprende módulos y paquetes en Python: imports, __init__.py, estructura de proyectos, ejecución con -m, imports relativos y errores circulares.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026