Un colega mío escribió un código análogo al siguiente de hoy, me pidió que echara un vistazo y me llevó un tiempo descubrir el error:
class A(): def __init__(self): print('A') class B(A): def __init__(self): super(B).__init__() b = B()
El problema aquí es que no hay ningún parámetro para super()
en el constructor de B
Lo que me sorprendió es que no pasa absolutamente nada en este caso, es decir, no hay error, nada. ¿Qué contiene el super
objeto creado por super(B)
? Como objeto, claramente tiene un constructor, así que eso es lo que se llama, pero ¿cómo se relaciona ese objeto con B
? En particular, ¿por qué es este código válido y no lanza una excepción en algún lugar? ¿Es super(B)
un objeto con algún uso real y cuál sería?
Lo único que causa todas estas ambigüedades es que “¿por qué obj = super(B).__init__()
funciona?”. Esto se debe a que super(B).__self_class__
devuelve None
y, en ese caso, está llamando a los objetos None
__init__
como a continuación, que devuelve Ninguno:
In [40]: None.__init__()
Con respecto al rest de los casos, simplemente puede verificar la diferencia llamando a los atributos esenciales del super
en ambos casos:
In [36]: class B(A): def __init__(self): obj = super(B, self) print(obj.__class__) print(obj.__thisclass__) print(obj.__self_class__) print(obj.__self__) ....: In [37]: b = B() <__main__.B object at 0x7f15a813f940> In [38]: In [38]: class B(A): def __init__(self): obj = super(B) print(obj.__class__) print(obj.__thisclass__) print(obj.__self_class__) print(obj.__self__) ....: In [39]: b = B() None None
Para el rest de las cosas le recomiendo que lea la documentación a fondo. https://docs.python.org/3/library/functions.html#super y este artículo de Raymond Hettinger https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ .
Además, si desea saber por qué super(B)
no funciona fuera de la clase y, en general, por qué llamar a super()
sin ningún argumento funciona dentro de una clase, puede leer Esta respuesta integral de Martijn https://stackoverflow.com / a / 19609168/2867928 .
Una breve descripción de la solución:
Como se menciona en los comentarios de @Nathan Vērzemnieks, debe llamar al inicializador una vez para que funcione el objeto super()
. El motivo se encuentra detrás de la magia del nuevo super
objeto que se explica en los enlaces mencionados anteriormente.
In [1]: class A: ...: def __init__(self): ...: print("finally!") ...: In [2]: class B(A): ...: def __init__(self): ...: sup = super(B) ...: print("Before: {}".format(sup)) ...: sup.__init__() ...: print("After: {}".format(sup)) ...: sup.__init__() ...: In [3]: B() Before: , NULL> After: , > finally!
La confusión aquí proviene del hecho de que (en un contexto de definición de clase) super()
proporciona un objeto super
enlazado que luego delega __init__
a su __self_class__
, mientras que super(B)
crea un objeto super
no __self_class__
que, debido a que __self_class__
es None
, no delegar
In [41]: class Test(int): ...: def __init__(self): ...: print(super().__self_class__) ...: print(super().__init__) ...: print(super(Test).__self_class__) ...: print(super(Test).__init__) ...: In [42]: Test() None
Por lo tanto, cuando llama a super(B).__init__()
, crea un super
__init__
pero inmediatamente llama a __init__
en él; eso, debido a la magia descrita en los diversos enlaces en esta otra respuesta , se une a ese super
sin super
. No hay referencias a él, por lo que desaparece, pero eso es lo que está sucediendo bajo el capó.