Historia del tema:
La función super() se introdujo para simplificar el trabajo con la herencia. Antes de su aparición, era necesario especificar explícitamente el nombre de la clase padre al llamar a los métodos de los antepasados, lo que hacía que la mezcla y la herencia múltiple fueran incómodas y podían llevar a errores. super() considera el orden de herencia (MRO).
Problema:
Con la herencia múltiple, la llamada directa a la clase padre a menudo solo invoca sus métodos, ignorando a otros antepasados. Esto rompe la cadena de llamadas. La tarea de super() es construir correctamente la cadena de métodos teniendo en cuenta la jerarquía.
Solución:
super() devuelve un objeto proxy que delega las llamadas a la siguiente clase en la línea de MRO. Esto permite invocar de manera uniforme los métodos de todos los antepasados en la cadena.
Ejemplo de código:
class A: def show(self): print("A.show()") class B(A): def show(self): print("B.show() -> ", end="") super().show() class C(A): def show(self): print("C.show() -> ", end="") super().show() class D(B, C): def show(self): print("D.show() -> ", end="") super().show() d = D() d.show() # Salida: D.show() -> B.show() -> C.show() -> A.show()
Características clave:
¿Se puede usar super sin argumentos en métodos estáticos?
No, en los métodos estáticos no hay ni self ni cls. super() espera ser llamado desde un método de instancia o clase, donde hay información sobre el tipo de objeto.
¿Se llamará al método padre si se omite en la cadena de llamadas a super()?
No. Si en la cadena no se llamó a super(), la ejecución no "se pasará" más allá. Por lo tanto, al anular métodos, es importante recordar siempre sobre super(), de lo contrario, parte de la lógica de los antepasados se perderá.
¿Se pueden pasar argumentos arbitrarios a super()?
Se puede, pero la forma normal de super() sin argumentos (en Python 3) ayuda a evitar errores y se ve más limpia. Pasar argumentos solo es necesario en casos raros (por ejemplo, compatibilidad con Python 2), y generalmente no es necesario.
Ventajas:
Caso negativo: En un gran proyecto, la clase padre contenía la inicialización, pero una de las clases hijas olvidó llamar a super().init(). Como resultado, aparecieron objetos parcialmente inicializados con atributos no inicializados.
Ventajas: no se requería llamar manualmente a cada clase padre. Desventajas: complejidad para rastrear si alguien omitió super().
Caso positivo: Una cadena de super() bien implementada aseguró la inicialización correcta de todas las clases base, simplificando el mantenimiento y desarrollo de la arquitectura.
Ventajas: limpieza del código, facilidad de extensión. Desventajas: es fácil cometer errores con un MRO complicado: se debe entender el orden de las llamadas.