Dans Python 3.x, super()
peut être appelé sans arguments:
class A(object):
def x(self):
print("Hey now")
class B(A):
def x(self):
super().x()
>>> B().x()
Hey now
Pour que cela fonctionne, une magie au moment de la compilation est effectuée, ce qui a pour conséquence que le code suivant (qui rebinde super
à super_
) Échoue:
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
Pourquoi super()
ne parvient-il pas à résoudre la super-classe au moment de l'exécution sans l'aide du compilateur? Existe-t-il des situations pratiques dans lesquelles ce comportement, ou la raison sous-jacente, pourrait mordre le programmeur imprudent?
... et, en guise de question de côté: y at-il d’autres exemples dans Python de fonctions, méthodes, etc.) qui peuvent être cassés en les rapprochant sous un nom différent?
Le nouveau comportement magique super()
a été ajouté pour éviter de violer le code D.R.Y. Principe (ne vous répétez pas), voir PEP 3135 . Le fait de nommer explicitement la classe en la référençant comme globale est également sujet aux mêmes problèmes de ré-association que vous avez découverts avec super()
class Foo(Bar):
def baz(self):
return super(Foo, self).baz() + 42
Spam = Foo
Foo = something_else()
Spam().baz() # liable to blow up
La même chose s'applique à l'utilisation de décorateurs de classe où le décorateur retourne un nouvel objet, qui renoue le nom de la classe:
@class_decorator_returning_new_class
class Foo(Bar):
def baz(self):
# Now `Foo` is a *different class*
return super(Foo, self).baz() + 42
La cellule magique super()
__class__
évite ces problèmes en vous donnant accès à l'objet de classe d'origine.
Le PEP a été lancé par Guido, qui initialement envisagé super
devenir un mot clé , et l’idée d’utiliser une cellule pour rechercher la classe actuelle était aussi le sien =. Certes, l'idée d'en faire un mot-clé faisait partie du première version du PEP .
Cependant, c’est en fait Guido lui-même qui alors s’éloignant de l’idée de mot clé comme étant "trop magique" , proposant à la place l’implémentation actuelle. Il prévoyait que l'utilisation d'un nom différent pour super()
pourrait être un problème :
Mon patch utilise une solution intermédiaire: il suppose que vous ayez besoin de
__class__
à chaque fois que vous utilisez une variable nommée'super'
. Ainsi, si vous renommez (globalement)super
ensupper
et utilisezsupper
mais passuper
, cela ne fonctionnera pas sans arguments (mais cela restera travaillez si vous passez soit__class__
ou l’objet de classe réel); si vous avez une variable non liée nomméesuper
, tout fonctionnera, mais la méthode utilisera le chemin d'appel légèrement plus lent utilisé pour les variables de cellule.
En fin de compte, c'est Guido lui-même qui a déclaré que l'utilisation d'un mot clé super
ne se sentait pas bien, et que fournir une cellule magique __class__
constituait un compromis acceptable.
Je conviens que le comportement magique et implicite de l'implémentation est quelque peu surprenant, mais super()
est l'une des fonctions les plus mal appliquées du langage. Il suffit de jeter un coup d'œil à tous les appels mal appliqués super(type(self), self)
ou super(self.__class__, self)
invocations trouvées sur Internet; si l'un de ces codes a déjà été appelé à partir d'une classe dérivée vous obtiendrez une exception de récursion infinie . À tout le moins, l'appel simplifié super()
, sans argument, évite ça problème.
Quant au renommé super_
; il suffit de référencer __class__
dans votre méthode aussi et ça fonctionnera à nouveau. La cellule est créée si vous référencez les noms super
ou __class__
de votre méthode:
>>> 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