¿Por qué la super() magia de Python 3.x?
En Python 3.x, super()
se puede llamar sin argumentos:
class A(object):
def x(self):
print("Hey now")
class B(A):
def x(self):
super().x()
>>> B().x()
Hey now
Para que esto funcione, se realiza algo de magia en tiempo de compilación, una consecuencia de la cual es que el siguiente código (que se vuelve a vincular 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 "<stdin>", line 1, in <module>
File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found
¿Por qué super()
no se puede resolver la superclase en tiempo de ejecución sin la ayuda del compilador? ¿Existen situaciones prácticas en las que este comportamiento, o la razón subyacente del mismo, podría afectar a un programador desprevenido?
... y, como pregunta paralela: ¿hay otros ejemplos en Python de funciones, métodos, etc. que puedan romperse volviendo a vincularlos con un nombre diferente?
El nuevo super()
comportamiento mágico se agregó para evitar violar el principio DRY (No repetirse), consulte PEP 3135 . Tener que nombrar explícitamente la clase haciendo referencia a ella como global también es propenso a sufrir los mismos problemas de vinculación que descubrió consigo super()
misma:
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 clases donde el decorador devuelve un nuevo objeto, que vuelve a vincular 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 mágica super()
__class__
evita estos problemas al darle acceso al objeto de clase original.
El PEP fue iniciado por Guido, quien inicialmente imaginó super
convertirse en una palabra clave , y la idea de usar una celda para buscar la clase actual también fue suya . Ciertamente, la idea de convertirlo en una palabra clave formaba parte del primer borrador del PEP .
Sin embargo, fue el propio Guido quien se alejó de la idea de la palabra clave por considerarla "demasiado mágica" y propuso en su lugar la implementación actual. Anticipó que usar un nombre diferente super()
podría ser un problema :
Mi parche usa una solución intermedia: asume que necesitas
__class__
cada vez que usas una variable llamada'super'
. Por lo tanto, si cambia el nombre (globalmente)super
asupper
y usasupper
but notsuper
, no funcionará sin argumentos (pero seguirá funcionando si lo pasa__class__
o el objeto de clase real); Si tiene una variable no relacionada llamadasuper
, todo funcionará, pero el método utilizará la ruta de llamada ligeramente más lenta que se utiliza para las variables de celda.
Entonces, al final, fue el propio Guido quien proclamó que usar una super
palabra clave no parecía correcto y que proporcionar una __class__
celda mágica era un compromiso aceptable.
Estoy de acuerdo en que el comportamiento mágico e implícito de la implementación es algo sorprendente, pero super()
es una de las funciones más mal aplicadas en el lenguaje. Basta con echar un vistazo a todos los mal aplicados super(type(self), self)
o super(self.__class__, self)
invocaciones que se encuentran en Internet; Si alguna vez se llamara algo de ese código desde una clase derivada, terminaría con una excepción de recursividad infinita . Como mínimo la super()
convocatoria simplificada, sin argumentos, evita ese problema.
En cuanto al renombrado super_
; simplemente haga referencia __class__
a su método también y funcionará nuevamente. 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