Comprendre les méthodes Python super() avec init () [dupliquer]
cette question a déjà une réponse ici:
j'essaie de comprendre l'utilisation de super()
. Apparemment, les deux classes d'enfants peuvent être créées, très bien.
je suis curieux de savoir à propos de la réelle différence entre les 2 classes enfant.
class Base(object):
def __init__(self):
print "Base created"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
7 réponses
super()
permet d'éviter de faire explicitement référence à la classe de base, ce qui peut être agréable. Mais le principal avantage vient avec l'héritage multiple, où toutes sortes de fun stuff peut se produire. Voir le docs standard sur super si vous ne l'avez pas déjà.
notez que la syntaxe a changé en Python 3.0 : vous pouvez simplement dire super().__init__()
au lieu de super(ChildB, self).__init__()
qui IMO est un peu plus sympa. Le la norme docs se réfère également à un guide d'utilisation de super () qui est très explicatif.
j'essaie de comprendre
super()
la raison pour laquelle nous utilisons super
est que les classes d'enfants qui peuvent utiliser l'héritage coopératif multiple appelleront la fonction correcte de classe parent suivante dans L'ordre de résolution de méthode (MRO).
en Python 3, Nous pouvons l'appeler comme ceci:
class ChildB(Base):
def __init__(self):
super().__init__()
en Python 2, nous devons l'utiliser comme ceci:
super(ChildB, self).__init__()
Sans super, vous êtes limité dans votre capacité à utiliser l'héritage multiple:
Base.__init__(self) # Avoid this.
Je m'explique plus en détail ci-dessous.
" quelle différence y a-t-il réellement dans ce code?: "
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
# super().__init__() # you can call super like this in Python 3!
la principale différence dans ce code est que vous obtenez une couche d'indirecte dans le __init__
avec super
, qui utilise la classe courante pour déterminer la prochaine classe __init__
pour regarder dans le MRO.
j'illustre cette différence dans une réponse à la question canonique , comment utiliser 'super' en Python? , qui démontre injection de dépendance et héritage multiple coopératif .
si Python n'avait pas super
voici le code qui est en fait très proche de super
(comment il est implémenté en C, moins certains comportements de vérification et de repli, et traduit en Python):
class ChildB(Base):
def __init__(self):
mro = type(self).mro() # Get the Method Resolution Order.
check_next = mro.index(ChildB) + 1 # Start looking after *this* class.
while check_next < len(mro):
next_class = mro[check_next]
if '__init__' in next_class.__dict__:
next_class.__init__(self)
break
check_next += 1
écrit un peu plus comme Python natif:
class ChildB(Base):
def __init__(self):
mro = type(self).mro()
for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
if hasattr(next_class, '__init__'):
next_class.__init__(self)
break
si nous n'avions pas l'objet super
, nous aurions à écrire ce code manuel partout (ou le recréer!) pour s'assurer que nous appelons la méthode suivante appropriée dans L'ordre de résolution de méthode!
comment super fait - il cela en Python 3 sans être dit explicitement quelle classe et instance de la méthode il a été appelé à partir?
il obtient le cadre de la pile d'appels, et trouve la classe (implicitement stockée comme une variable libre locale, __class__
, faisant de la fonction d'appel une fermeture sur la classe) et le premier argument de cette fonction, qui devrait être l'instance ou la classe qui l'informe de la méthode D'ordre de résolution (MRO) à utiliser.
Puisqu'il exige que le premier argument pour le MRO, en utilisant super
avec des méthodes statiques est impossible .
critiques d'autres réponses:
super () vous permet d'éviter de faire référence à la classe de base explicitement, ce qui peut être agréable. . Mais le principal avantage vient de l'héritage multiple, où toutes sortes de choses peuvent arriver. Voir les docs standard sur super si vous ne l'avez pas déjà.
c'est plutôt ondulé à la main et ça ne nous dit pas grand chose, mais le but de super
n'est pas d'éviter d'écrire la classe des parents. Il s'agit de s'assurer que la prochaine méthode dans l'ordre de la méthode de résolution (MRO) est appelée. Cela devient important dans l'héritage multiple.
je vais vous expliquer.
class Base(object):
def __init__(self):
print("Base init'ed")
class ChildA(Base):
def __init__(self):
print("ChildA init'ed")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("ChildB init'ed")
super(ChildB, self).__init__()
et créons une dépendance que nous voulons appeler comme L'enfant:
class UserDependency(Base):
def __init__(self):
print("UserDependency init'ed")
super(UserDependency, self).__init__()
maintenant rappelez-vous, ChildB
utilise super, ChildA
n'est pas:
class UserA(ChildA, UserDependency):
def __init__(self):
print("UserA init'ed")
super(UserA, self).__init__()
class UserB(ChildB, UserDependency):
def __init__(self):
print("UserB init'ed")
super(UserB, self).__init__()
et UserA
n'appelle pas la méthode de dépendance Userd:
>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>
mais UserB
, parce que ChildB
utilise super
, fait!:
>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>
les Critiques pour une autre réponse
en aucun cas vous ne devez faire ce qui suit, ce qu'une autre réponse suggère, car vous obtiendrez certainement des erreurs lorsque vous sous-classe ChildB:
super(self.__class__, self).__init__() # Don't do this. Ever.
(cette réponse n'est pas intelligente ni particulièrement intéressante, mais en dépit de la critique directe dans les commentaires et plus de 17 commentaires négatifs, le répondeur a persisté à La suggérer jusqu'à ce qu'un gentil rédacteur ait réglé son problème.)
explication: cette réponse suggère d'appeler super comme ceci:
super(self.__class__, self).__init__()
c'est complètement faux. super
permet de regarder le prochain parent dans le MRO (voir la première section de cette réponse) pour les classes pour enfants. Si vous dites super
nous sommes dans la méthode de l'instance de l'enfant, il va alors chercher la méthode suivante en ligne (probablement celle-ci) résultant en une récursion, causant probablement un échec logique (dans l'exemple du répondeur, il fait) ou un RuntimeError
lorsque la profondeur de la récursion est dépassée.
>>> class Polygon(object):
... def __init__(self, id):
... self.id = id
...
>>> class Rectangle(Polygon):
... def __init__(self, id, width, height):
... super(self.__class__, self).__init__(id)
... self.shape = (width, height)
...
>>> class Square(Rectangle):
... pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'
il a été noté que dans Python 3.0+ vous pouvez utiliser
super().__init__()
pour faire votre appel, qui est concis et ne vous oblige pas à faire référence aux noms de parent ou de classe explicitement, ce qui peut être pratique. Je veux juste ajouter que pour Python 2.7 ou moins, il est possible d'obtenir ce comportement insensible au nom en écrivant self.__class__
au lieu du nom de classe, i.e.
super(self.__class__, self).__init__()
cependant, cela brise les appels à super
pour toutes les classes qui héritent de votre classe, où self.__class__
pourrait retourner une classe d'enfant. Par exemple:
class Polygon(object):
def __init__(self, id):
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id)
self.shape = (width, height)
class Square(Rectangle):
pass
Ici, j'ai une classe Square
, qui est une sous-classe de Rectangle
. Disons que je ne veux pas écrire un constructeur séparé pour Square
parce que le constructeur pour Rectangle
est suffisant, mais pour quelque raison que ce soit je veux implémenter un carré pour que je puisse réimposer une autre méthode.
Quand Je créer un Square
en utilisant mSquare = Square('a', 10,10)
, Python appelle le constructeur pour Rectangle
parce que je n'ai pas donné Square
à son propre constructeur. Cependant, dans le constructeur pour Rectangle
, l'appel super(self.__class__,self)
va retourner la superclasse de mSquare
, donc il appelle le constructeur pour Rectangle
à nouveau. C'est ainsi que la boucle infinie se produit, comme l'a mentionné @s_c. Dans ce cas, quand j'exécute super(...).__init__()
j'appelle le constructeur pour Rectangle
mais puisque je donne il n'y a pas d'arguments, je vais avoir une erreur.
Super n'a pas d'effets secondaires
Base = ChildB
Base()
fonctionne comme prévu
Base = ChildA
Base()
entre en récursion infinie.
juste un avertissement... avec Python 2.7, et je crois que depuis que super()
a été introduit dans la version 2.2, on ne peut appeler super()
que si l'un des parents hérite d'une classe qui hérite finalement object
( new-style classes ).
personnellement, comme pour le code python 2.7, je vais continuer à utiliser BaseClassName.__init__(self, args)
jusqu'à ce que j'obtienne l'avantage d'utiliser super()
.
il n'y en a pas, vraiment. super()
examine la classe suivante dans le MRO (method resolution order, accessed with cls.__mro__
) pour appeler les méthodes. Il suffit d'appeler la base __init__
pour appeler la base __init__
. Il se trouve que le MRO A exactement un élément-- la base. Donc, vous faites vraiment la même chose, mais d'une manière plus agréable avec super()
(en particulier si vous obtenez dans l'héritage multiple plus tard).
la principale différence est que ChildA.__init__
appellera inconditionnellement Base.__init__
tandis que ChildB.__init__
appellera __init__
dans quelle que soit la classe qui se trouve être ChildB
ancêtre dans self
ligne des ancêtres de
(qui peuvent différer de ce que vous attendez).
si vous ajoutez un ClassC
qui utilise l'héritage multiple:
class Mixin(Base):
def __init__(self):
print "Mixin stuff"
super(Mixin, self).__init__()
class ChildC(ChildB, Mixin): # Mixin is now between ChildB and Base
pass
ChildC()
help(ChildC) # shows that the the Method Resolution Order is ChildC->ChildB->Mixin->Base
puis Base
n'est plus le parent de ChildB
pour ChildC
instances. Maintenant super(ChildB, self)
pointera vers Mixin
si self
est une instance ChildC
.
Vous avez inséré Mixin
entre ChildB
et Base
. Et vous pouvez en profiter avec super()
donc si vous êtes conçu vos classes de sorte qu'elles puissent être utilisées dans un scénario coopératif D'héritage Multiple, vous utilisez super
parce que vous ne faites pas vraiment de savoir qui va être l'ancêtre lors de l'exécution.
le super considered super post et pycon 2015 accompagnant vidéo expliquer assez bien.