super() en Python: herencia, MRO y mixins

Actualizado el: 20/08/2026
Tempo de leitura: 5 minutos
Uso do super em Python para resolver problemas de herança

super() permite acceder a métodos de la siguiente clase en el orden de resolución de métodos. Se utiliza con frecuencia dentro de __init__ para inicializar correctamente una clase padre, pero su función es más amplia: ayuda a construir jerarquías cooperativas, reutilizar comportamiento y manejar herencia múltiple sin llamar clases concretas de forma rígida.

Antes de profundizar, conviene repasar la programación orientada a objetos, la guía específica para usar super(), los parámetros *args y **kwargs, las funciones y los decoradores.

La documentación oficial de super() explica el objeto proxy y la búsqueda de atributos. La referencia del modelo de datos ayuda a entender clases, instancias y resolución de métodos.

El problema que resuelve super()

Imagina una clase base:

class Persona:
    def __init__(self, nombre):
        self.nombre = nombre

    def describir(self):
        return f"Persona: {self.nombre}"

Una subclase necesita conservar la inicialización del nombre y añadir un atributo:

class Empleado(Persona):
    def __init__(self, nombre, puesto):
        super().__init__(nombre)
        self.puesto = puesto

super().__init__(nombre) busca el siguiente __init__ según el MRO y lo ejecuta con la instancia actual. No necesitas pasar self explícitamente.

Por qué no llamar directamente a la clase padre

class Empleado(Persona):
    def __init__(self, nombre, puesto):
        Persona.__init__(self, nombre)
        self.puesto = puesto

Esta forma puede funcionar en una herencia simple, pero acopla Empleado a Persona. Si cambia la jerarquía, la llamada puede quedar incorrecta. En herencia múltiple, llamar padres directamente también puede ejecutar una clase dos veces o saltarse otra.

Extender un método

class Empleado(Persona):
    def __init__(self, nombre, puesto):
        super().__init__(nombre)
        self.puesto = puesto

    def describir(self):
        base = super().describir()
        return f"{base}, puesto: {self.puesto}"


empleado = Empleado("Ana", "Desarrolladora")
print(empleado.describir())

La subclase reutiliza el resultado base y añade información. Esto evita duplicar el formato definido por la clase superior.

Qué es el MRO

El Method Resolution Order determina el orden de búsqueda:

print(Empleado.mro())
# [Empleado, Persona, object]

super() no significa necesariamente “mi padre directo”. Significa “continúa desde la clase actual en el MRO”. Esta diferencia es esencial en herencia múltiple.

Herencia múltiple cooperativa

class Registrable:
    def __init__(self, **kwargs):
        self.registros = []
        super().__init__(**kwargs)


class Identificable:
    def __init__(self, id_usuario, **kwargs):
        self.id_usuario = id_usuario
        super().__init__(**kwargs)


class Usuario(Registrable, Identificable):
    def __init__(self, nombre, **kwargs):
        self.nombre = nombre
        super().__init__(**kwargs)


usuario = Usuario(nombre="Ana", id_usuario=10)
print(Usuario.mro())

Cada clase consume sus propios argumentos y pasa los restantes. Finalmente, object.__init__ recibe una llamada sin argumentos adicionales. Para que el patrón funcione, todas las clases participantes deben cooperar.

Diseñar inicializadores compatibles

Una estrategia es utilizar parámetros nombrados y **kwargs:

class ConFecha:
    def __init__(self, fecha_creacion=None, **kwargs):
        self.fecha_creacion = fecha_creacion
        super().__init__(**kwargs)

No captures errores de argumentos para ocultarlos. Si queda un parámetro desconocido al final, es mejor recibir un TypeError que perder silenciosamente información.

El diamante de herencia

class A:
    def accion(self):
        print("A")


class B(A):
    def accion(self):
        print("B")
        super().accion()


class C(A):
    def accion(self):
        print("C")
        super().accion()


class D(B, C):
    def accion(self):
        print("D")
        super().accion()


D().accion()
print(D.mro())

El resultado sigue el MRO: D, B, C y A. A se ejecuta una sola vez. Si B y C llamaran directamente a A.accion(self), A podría ejecutarse dos veces.

super() en métodos de clase

class Documento:
    @classmethod
    def crear(cls, datos):
        return cls(datos)


class Factura(Documento):
    @classmethod
    def crear(cls, datos):
        datos_limpios = datos.strip()
        return super().crear(datos_limpios)

super() también funciona en métodos de clase porque Python conoce la clase actual y el primer argumento cls.

Uso explícito de super

La forma moderna sin argumentos es la más común dentro de una definición de clase:

super().metodo()

También existe una forma explícita:

super(Empleado, instancia).describir()

Es útil en casos avanzados, depuración o cuando se necesita iniciar la búsqueda desde un punto específico. Usarla incorrectamente puede saltarse clases, por lo que no es la primera opción.

Errores dentro de funciones anidadas

La forma de cero argumentos depende de un contexto creado por el compilador. Puede fallar dentro de una función anidada que no conserva adecuadamente ese contexto:

class Ejemplo(Persona):
    def metodo(self):
        def interna():
            return super().describir()
        return interna()

Para evitar ambigüedad, realiza la llamada en el método externo o pasa explícitamente el resultado necesario.

Cuando una clase no llama a super()

class Mezcla:
    def __init__(self, **kwargs):
        self.activo = True
        # Falta super().__init__(**kwargs)

Si aparece en una cadena cooperativa, las clases posteriores nunca se inicializan. Este error puede manifestarse como un atributo ausente mucho después. Revisa el MRO y confirma que cada clase delega.

Evitar firmas incompatibles

Estas clases son difíciles de combinar:

class A:
    def __init__(self, nombre):
        super().__init__()


class B:
    def __init__(self, edad):
        super().__init__()

En herencia múltiple, una clase puede consumir argumentos que la siguiente necesita. El patrón con parámetros nombrados y **kwargs hace explícito el flujo, aunque debe documentarse.

Composición frente a herencia

No uses herencia solo para reutilizar dos métodos. Si una clase “tiene” un servicio en lugar de “ser” una especialización, la composición suele ser más clara:

class Repositorio:
    def guardar(self, dato):
        print("Guardando", dato)


class ServicioUsuarios:
    def __init__(self, repositorio):
        self.repositorio = repositorio

super() ayuda a gestionar una jerarquía válida, pero no convierte una jerarquía innecesaria en un buen diseño.

Mixins

Un mixin aporta un comportamiento pequeño y no suele representar una entidad completa:

class JsonMixin:
    def como_dict(self):
        return self.__dict__.copy()

Si un mixin define __init__, debe cooperar con super(). Mantén los mixins pequeños, sin estado cuando sea posible y con nombres que indiquen su función.

Pruebas para la jerarquía

def test_usuario_inicializa_todas_las_clases():
    usuario = Usuario(nombre="Ana", id_usuario=10)
    assert usuario.nombre == "Ana"
    assert usuario.id_usuario == 10
    assert usuario.registros == []

También puedes registrar temporalmente cada inicializador para confirmar el orden. No bases la aplicación en el orden sin documentarlo y sin pruebas.

Inspeccionar el MRO

for clase in Usuario.__mro__:
    print(clase.__name__)

Hazlo cuando un método inesperado se ejecuta o cuando un atributo no se inicializa. El orden depende de cómo declaras las bases y del algoritmo C3 que Python utiliza para mantener una jerarquía consistente.

Errores frecuentes

Los problemas más comunes son olvidar super().__init__, pasar self manualmente, llamar una clase concreta dentro de una jerarquía múltiple, no reenviar argumentos, mezclar clases cooperativas con no cooperativas y asumir que super() siempre llama al padre escrito primero visualmente. También es un error construir herencia profunda cuando la composición sería más sencilla.

Reglas prácticas

Usa la forma super() sin argumentos dentro de métodos normales. Diseña firmas compatibles en jerarquías múltiples. Haz que cada clase consuma sus argumentos y delegue los restantes. Revisa mro() cuando haya dudas. Mantén la herencia poco profunda y añade pruebas que confirmen la inicialización completa.

Conclusión

super() continúa la búsqueda en el MRO y permite que varias clases colaboren sin llamadas rígidas. En herencia simple reduce acoplamiento; en herencia múltiple evita duplicaciones y saltos cuando todas las clases cooperan. Comprender el MRO, reenviar argumentos y saber cuándo elegir composición son las claves para usarlo sin errores.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código en un monitor que representa acceso a memoria nativa con ctypes.memoryview_at en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ctypes.memoryview_at: memoria nativa sin copias

    Aprende ctypes.memoryview_at en Python para acceder a memoria nativa sin copias, validar punteros y reducir riesgos.

    Ler mais

    Tempo de leitura: 7 minutos
    12/10/2026
    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