¿Por qué es la magia súper () de Python 3.x?

En Python 3.x, se puede llamar a super() sin argumentos:

 class A(object): def x(self): print("Hey now") class B(A): def x(self): super().x() 
 >>> B().x() Hey now 

Para hacer este trabajo, se realiza algo de magia en tiempo de comstackción, una consecuencia de la cual es que el siguiente código (que vuelve a super_ super a super_ ) falla:

 super_ = super class A(object): def x(self): print("No flipping") class B(A): def x(self): super_().x() 
 >>> B().x() Traceback (most recent call last): File "", line 1, in  File "", line 3, in x RuntimeError: super(): __class__ cell not found 

¿Por qué super() no puede resolver la superclase en tiempo de ejecución sin la asistencia del comstackdor? ¿Hay situaciones prácticas en las que este comportamiento, o la razón subyacente para ello, podría morder a un progtwigdor incauto?

… y, como una pregunta complementaria: ¿hay otros ejemplos en Python de funciones, métodos, etc. que puedan romperse volviéndolos a unir un nombre diferente?

El nuevo comportamiento de la magia super() se agregó para evitar violar el principio DRY (No se repita), vea PEP 3135 . Tener que nombrar explícitamente a la clase al hacer referencia a ella como global también es propenso a los mismos problemas de reencuadernación que descubrió con super() :

 class Foo(Bar): def baz(self): return super(Foo, self).baz() + 42 Spam = Foo Foo = something_else() Spam().baz() # liable to blow up 

Lo mismo se aplica al uso de decoradores de clase donde el decorador devuelve un nuevo objeto, que vuelve a unir el nombre de la clase:

 @class_decorator_returning_new_class class Foo(Bar): def baz(self): # Now `Foo` is a *different class* return super(Foo, self).baz() + 42 

La celda magic super() __class__ evita estos problemas muy bien al brindarle acceso al objeto de clase original.

El PEP fue iniciado por Guido, quien inicialmente imaginó que el super convertiría en una palabra clave , y la idea de usar una celda para buscar la clase actual también fue suya . Ciertamente, la idea de convertirla en una palabra clave fue parte del primer borrador del PEP .

Sin embargo, fue en realidad el mismo Guido quien luego se alejó de la idea de la palabra clave como “demasiado mágica” , proponiendo la implementación actual en su lugar. Él anticipó que usar un nombre diferente para super() podría ser un problema :

Mi parche usa una solución intermedia: asume que necesitas __class__ siempre que uses una variable llamada 'super' . Por lo tanto, si (globalmente) cambia super nombre de super a supper y usa supper pero no super , no funcionará sin argumentos (pero seguirá funcionando si lo pasa __class__ o el objeto de clase real); Si tiene una variable no relacionada llamada super , las cosas funcionarán, pero el método usará la ruta de llamada ligeramente más lenta utilizada para las variables de celda.

Entonces, al final, fue el mismo Guido el que proclamó que usar una super palabra clave no se sentía bien, y que proporcionar una celda mágica de __class__ era un compromiso aceptable.

Estoy de acuerdo en que la magia, el comportamiento implícito de la implementación es algo sorprendente, pero super() es una de las funciones más mal aplicadas en el lenguaje. Solo eche un vistazo a todas las invocaciones de super(type(self), self) o super(self.__class__, self) encontradas en Internet; si alguna vez se llamara a ese código desde una clase derivada , terminaría con una excepción de recursión infinita . Por lo menos la llamada super() simplificada, sin argumentos, evita ese problema.

En cuanto a los super_ renombrados; simplemente __class__ referencia a __class__ en su método también y funcionará de nuevo. La celda se crea si hace referencia a los nombres super o __class__ en su método:

 >>> super_ = super >>> class A(object): ... def x(self): ... print("No flipping") ... >>> class B(A): ... def x(self): ... __class__ # just referencing it is enough ... super_().x() ... >>> B().x() No flipping