3195 votes

Comprendre les méthodes Python super() et __init__()

J'essaie de comprendre l'utilisation de super() . En apparence, les deux classes enfant peuvent être créées, sans problème.

Je suis curieux de savoir quelle est la différence réelle entre les deux classes d'enfants suivantes.

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()

28 votes

Il s'agit d'une introduction très simple aux cours qui vaut la peine d'être suivie : realpython.com/python-super/ . C'est plus facile à digérer que les réponses données qui sont pour la plupart d'entre nous je suppose trop détaillées dans l'implémentation de python. Il comporte également des exemples pour rendre le tout concret.

2309voto

Kiv Points 9116

super() vous permet d'éviter de vous référer explicitement à la classe de base, ce qui peut être agréable. Mais l'avantage principal vient de l'héritage multiple, où toutes sortes d'éléments de la classe de base peuvent être utilisés. trucs amusants peut se produire. Voir le docs standard sur super si vous ne l'avez pas déjà fait.

Notez que la syntaxe a changé dans Python 3.0 : vous pouvez simplement dire super().__init__() au lieu de super(ChildB, self).__init__() qui, selon l'OMI, est beaucoup plus agréable. La documentation standard fait également référence à un guide d'utilisation super() qui est assez explicite.

51 votes

Pouvez-vous fournir un exemple de super() utilisé avec des arguments ?

29 votes

Pouvez-vous expliquer super(ChildB, self).__init__() ça, qu'est-ce que ChildB y self ont à voir avec le super

13 votes

@rimiro La syntaxe de super() est la suivante super([type [, object]]) Cela renverra la superclasse de type . Ainsi, dans ce cas, la superclasse de ChildB sera renvoyé. Si le deuxième argument est omis, le super objet retourné est non lié. Si le deuxième argument est un objet, alors isinstance(object, type) doit être vrai.

281voto

AnjoMan Points 410

Il a été noté que dans Python 3.0+ vous pouvez utiliser

super().__init__()

pour effectuer votre appel, ce qui est concis et ne vous oblige pas à référencer explicitement les noms des classes OR parentes, ce qui peut être pratique. Je veux juste ajouter que pour Python 2.7 ou moins, certaines personnes implémentent un comportement insensible au nom en écrivant self.__class__ au lieu du nom de la classe, c'est à dire

super(self.__class__, self).__init__()  # DON'T DO THIS!

CEPENDANT, cela interrompt les appels à super pour toutes les classes qui héritent de votre classe, où self.__class__ pourrait renvoyer une classe 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 car le constructeur de Rectangle est suffisant, mais pour une raison quelconque, je veux implémenter un carré pour pouvoir réimplémenter une autre méthode.

Lorsque je crée un Square en utilisant mSquare = Square('a', 10,10) Python appelle le constructeur de Rectangle parce que je n'ai pas donné Square son propre constructeur. Cependant, dans le constructeur de Rectangle l'appel super(self.__class__,self) va retourner la superclasse de mSquare et appelle donc le constructeur de Rectangle à nouveau. C'est ainsi que la boucle infinie se produit, comme l'a mentionné @S_C. Dans ce cas, lorsque j'exécute super(...).__init__() J'appelle le constructeur de Rectangle mais comme je ne lui donne aucun argument, j'obtiendrai une erreur.

46 votes

Ce que cette réponse suggère, super(self.__class__, self).__init__() ne fonctionne pas si vous sous-classez à nouveau sans fournir une nouvelle __init__ . On a alors une récursion infinie.

26 votes

Cette réponse est ridicule. Si vous abusez de super de cette façon, vous pouvez aussi bien coder en dur le nom de la classe de base. C'est moins faux que cela. La raison d'être du premier argument de super est qu'il est no nécessairement le type de soi. Veuillez lire "super considered super" de Rhettinger (ou regardez certaines de ses vidéos).

7 votes

Le raccourci démontré ici pour Python 2 comporte des pièges qui ont déjà été mentionnés. Ne l'utilisez pas, ou votre code se cassera d'une manière que vous ne pouvez pas prévoir. Ce "raccourci pratique" casse super, mais vous ne vous en rendrez compte qu'après avoir passé beaucoup de temps à déboguer. Utilisez Python 3 si super est trop verbeux.

83voto

S C Points 439

Super n'a pas d'effets secondaires

Base = ChildB

Base()

fonctionne comme prévu

Base = ChildA

Base()

entre dans une récursion infinie.

8 votes

L'affirmation "Super n'a pas d'effets secondaires" n'a pas de sens dans ce contexte. Super garantit simplement que nous appelons la méthode correcte de la classe suivante dans l'ordre de résolution des méthodes, alors que l'autre façon de faire code en dur la méthode suivante à appeler, ce qui rend l'héritage multiple coopératif plus difficile.

6 votes

Cette réponse est fragmentaire (les exemples de code n'ont de sens que dans la continuité du code de la réponse).

77voto

rgenito Points 498

Juste une info... avec Python 2.7, et je crois depuis lors super() a été introduit dans la version 2.2, vous pouvez seulement appeler super() si l'un des parents hérite d'une classe qui hérite éventuellement de object ( des classes d'un nouveau genre ).

Personnellement, en ce qui concerne le code python 2.7, je vais continuer à utiliser BaseClassName.__init__(self, args) jusqu'à ce que j'obtienne l'avantage d'utiliser super() .

5 votes

Très bon point. SI vous ne mentionnez pas clairement : class Base(object) : alors vous obtiendrez une erreur comme celle-ci : "Erreur de type : doit être type, pas classbj"

1 votes

@andi J'ai eu cette erreur l'autre jour et j'ai fini par renoncer à essayer de la résoudre. J'étais juste en train de faire des bêtises sur iPython. Quel cauchemar pour un mauvais message d'erreur si c'était le code que je devais déboguer !

61voto

Devin Jeanpierre Points 23162

Il n'y en a pas, vraiment. super() examine la classe suivante dans l'ordre de résolution des méthodes (MRO, method resolution order), auquel on accède par le bouton cls.__mro__ ) pour appeler les méthodes. Le simple fait d'appeler la base __init__ appelle la base __init__ . Il se trouve que le MRO a exactement un élément - la base. Donc vous faites exactement la même chose, mais d'une manière plus agréable avec super() (en particulier si vous vous lancez dans l'héritage multiple par la suite).

3 votes

Je vois. Pourriez-vous expliquer un peu pourquoi il est plus intéressant d'utiliser super() avec l'héritage multiple ? Pour moi, la base.__init__(self) est plus courte (plus propre). Si j'avais deux classes de base, il y aurait deux de ces lignes, ou deux lignes super(). Ou ai-je mal compris ce que vous vouliez dire par "plus joli" ?

8 votes

En fait, il s'agirait d'une seule ligne super(). Lorsque vous avez un héritage multiple, le MRO est toujours plat. Donc le premier appel à super().__init__ appelle la classe suivante init qui appelle ensuite la suivante, et ainsi de suite. Vous devriez vraiment consulter des documents à ce sujet.

0 votes

Le MRO de la classe enfant contient également l'objet - le MRO d'une classe est visible dans l'onglet mro variable de classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X