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

Publicado el: 11/07/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

    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
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    bz2 en Python: comprime con bzip2

    Aprende bz2 en Python para comprimir archivos y bytes, procesar streams por bloques y limitar la expansión de datos externos.

    Ler mais

    Tempo de leitura: 4 minutos
    16/08/2026