¿Cuál es la diferencia entre super () y el nombre de clase principal?

¿Hay alguna diferencia entre usar super() y usar el nombre de la clase padre directamente? Por ejemplo:

 class Parent: def __init__(self): print("In parent") self.__a=10 class Child(Parent): def __init__(self): super().__init__() # using super() Parent.__init__(self) # using Parent class name c=Child() 

¿Hay una diferencia interna entre super().__init__() y Parent.__init__(self) ?

No en este caso . Pero en general , y especialmente cuando usa la herencia múltiple , super() delega al siguiente objeto en la Orden de resolución de métodos (MRO) como se especifica en la documentación :

super([type[, object-or-type]])

Devuelva un objeto proxy que delegue las llamadas de método a una clase de tipo padre o hermana. Esto es útil para acceder a los métodos heredados que se han anulado en una clase. El orden de búsqueda es el mismo que el utilizado por getattr() excepto que se omite el tipo en sí.

El atributo __mro__ del tipo enumera el orden de búsqueda de resolución de método utilizado por getattr() y super() . El atributo es dynamic y puede cambiar siempre que se actualice la jerarquía de herencia.

(…)

(Copiado, negrita añadida)

Digamos, por ejemplo, que define clases como (tomadas de esta pregunta, donde se discute con mayor detalle el MRO ):

 class F: def __init__(self): print('F%s'%super().__init__) super().__init__() class G: def __init__(self): print('G%s'%super().__init__) super().__init__() class H: def __init__(self): print('H%s'%super().__init__) super().__init__() class E(G,H): def __init__(self): print('E%s'%super().__init__) super().__init__() class D(E,F): def __init__(self): print('D%s'%super().__init__) super().__init__() class C(E,G): def __init__(self): print('C%s'%super().__init__) super().__init__() class B(C,H): def __init__(self): print('B%s'%super().__init__) super().__init__() class A(D,B,E): def __init__(self): print('A%s'%super().__init__) super().__init__() 

Entonces el __mro__ de A es:

 A.__mro__ == (A,D,B,C,E,G,H,F,object) 

Ahora bien, si llamamos a A() , se imprime:

 A> D> B> C> E> G> H> F <__main__.A object at 0x7efefd8645c0> 

por lo que significa que en el contexto de A y al intentar obtener __init__ que:

  • super().__init__ de A es D.__init__ ;
  • super().__init__ de D es B.__init__ ;
  • super().__init__ de B es C.__init__ ;
  • super().__init__ de C es E.__init__ ;
  • super().__init__ de E es G.__init__ ;
  • super().__init__ de G es H.__init__ ;
  • super().__init__ de H es F.__init__ ; y
  • super().__init__ of F es object.__init__ .

Tenga en cuenta, por lo tanto, que super() no per se delega a un padre . Por ejemplo, el super() de D es B y B no es una superclase de D , por lo que realmente depende del tipo de objeto (no de la clase).

Ahora en el caso de D , el __mro__ es:

 D.__mro__ = (D,E,G,H,F,object) 

Si construimos una D sin embargo obtenemos:

 D> E> G> H> F 

Así que en el contexto de D sostiene que:

  • super().__init__ de D es E.__init__ ;
  • super().__init__ de E es G.__init__ ;
  • super().__init__ de G es H.__init__ ;
  • super().__init__ de H es F.__init__ ; y
  • super().__init__ of F es object.__init__ .

Entonces aquí el super() de D lleva a E (para __init__ ) que no es lo mismo en el contexto de A

 super().__init__(*args, **kwargs) 

Percibes que no pasas el “yo” – se inserta automáticamente.

super() se diseñó por primera vez en Python 2 para permitir que las clases se reutilizaran como mixins en una jerarquía de clases de manera que su superclase inmediata pueda cambiar:

Supongamos que en algún momento su código es como:

 class A: pass class B(A): def __init__(self, *args, **kwargs): ... # Fixed call to A A.__init__(self, *args, **kwargs) class C(A): def __init__(self, *args, **kwargs): ... # Fixed call to A A.__init__(self, *args, **kwargs) class D(C, B): pass 

En este punto, el código OOP correcto debe ejecutar C.__init__ que debe encadenar la llamada a B.__init__ : pero cuando el nombre de la superclase está codificado de forma rígida, eso no sucede; el __init__ siempre vendría después. Y si codificas B.__init__ en C , B.__init__ que C funcione sin B , anulando el propósito de la herencia múltiple.

Cuando usas super() lugar, Python’s realiza la búsqueda del método para la siguiente clase padre buscando el atributo __mro__ la clase (mro = orden de resolución del método. __mro__ es un atributo concreto asociado a cada clase de Python). – Por lo tanto, si en algún momento en el tiempo la clase D anterior ya no hereda de B , las llamadas a super().__init__ en C se redireccionarán automáticamente a A

También vale la pena señalar que en Python 3 se introdujo la forma sin parámetros de super para facilitar su uso; antes de eso, uno tenía que codificar una referencia a la propia clase y también insertarse self en los parámetros. Esta forma es una de las pocas excepciones en Python que está codificada en el propio comstackdor: cambia las cosas internamente en los métodos cuando se ve super (o __class__ ) dentro de un cuerpo del método (a saber, crea una variable __class__ que apunta a la propia clase que utiliza la super llamada)