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

    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
    logo do python com objetos abaixo do logo
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Programación orientada a objetos en Python

    Aprende programación orientada a objetos en Python con clases, objetos, atributos, métodos, propiedades, herencia, composición y ejemplos prácticos.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Programação assíncrona com asyncio em Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    asyncio en Python: guía práctica para principiantes

    Aprende asyncio en Python con coroutines, await, tasks, TaskGroup, timeouts, cancelación, colas, bloqueos y manejo de errores.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026