56 votes

Comment le "super" Python fait-il la bonne chose?

Je suis sous Python 2.5, de sorte que cette question ne s'applique pas à Python 3. Lorsque vous effectuez un diamant hiérarchie de classe en utilisant l'héritage multiple et de créer un objet de la plus dérivée de la classe, Python fait la bonne Chose (TM). Il appelle le constructeur de la plus dérivée de la classe, puis de ses classes parentes présentées de gauche à droite, puis la grand-parent. Je suis familier avec Python MRO, ce n'est pas ma question. Je suis curieux de voir comment l'objet renvoyé de super gère réellement à communiquer aux appels de super dans les classes parent le bon ordre. Prenons cet exemple de code:

#!/usr/bin/python

class A(object):
    def __init__(self): print "A init"

class B(A):
    def __init__(self):
    	print "B init"
    	super(B, self).__init__()

class C(A):
    def __init__(self):
    	print "C init"
    	super(C, self).__init__()

class D(B, C):
    def __init__(self):
    	print "D init"
    	super(D, self).__init__()

x = D()

Le code ne l'intuitif chose, il imprime:

D init
B init
C init
A init

Cependant, si vous en commentaire l'appel à super pour B de la fonction init, ni Une ni C de la fonction init est appelée. Cela signifie B de l'appel à super est en quelque sorte la conscience du C de l'existence dans l'ensemble de la hiérarchie de classe. Je sais que super retourne un objet proxy avec une surcharge obtenir de l'opérateur, mais comment fait l'objet renvoyé par super en D init définition de communiquer l'existence de C de l'objet renvoyé par super en B init définition? L'information est que les appels ultérieurs de super utiliser stockées sur l'objet lui-même? Si oui, pourquoi n'est-ce pas super au lieu de soi.super?

Edit: Jekke tout à fait raison de rappeler qu'il n'est pas de soi.super parce que super est un attribut de la classe, et non une instance de la classe. Conceptuellement, cela a du sens, mais dans la pratique, super n'est pas un attribut de la classe! Vous pouvez le tester à l'interprète de faire deux classes A et B, où B hérite de A, et en appelant dir(B). Il n'a pas d' super ou __super__ attributs.

34voto

Jacob Gabrielson Points 8800

Modifiez votre code pour ceci et je pense que cela expliquera les choses (vraisemblablement super cherche où, par exemple, B trouve dans le __mro__ ?):

 class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()
 

Si vous l'exécutez, vous verrez:

 D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
 

Aussi, ça vaut le coup de jeter un œil à Super de Python, c'est chouette, mais vous ne pouvez pas l'utiliser .

16voto

batbrat Points 3481

J'ai fourni un tas de liens ci-dessous, la réponse à votre question plus en détail et plus précisément que je peux espérer. Je vais cependant donner une réponse à votre question dans mes propres mots, pour vous faire économiser du temps. Je vais mettre des points

  1. super est une interface de fonction, pas un attribut.
  2. Chaque type (classe) en Python a un __mro__ attribut qui stocke la méthode de résolution de l'ordre de cette instance.
  3. Chaque appel à super est de la forme super(type[, de l'objet ou de type]). Supposons que le deuxième attribut est un objet pour le moment.
  4. Le point de départ de super appels, l'objet est du type de la classe Dérivée (dire DC).
  5. super recherche des méthodes qui correspondent (dans votre cas __init__) dans les classes de l'MRO, après la classe spécifiée comme premier argument (dans ce cas de classes après la DC).
  6. Lorsque la méthode de correspondance est trouvée (disons dans la catégorie BC1), il est appelé.
    (Cette méthode doit utiliser super, donc je suis en supposant qu'il n' - Voir le Python est super chouette, mais ne peut pas être utilisé - lien ci-dessous) Cette méthode provoque alors une recherche dans la classe de l'objet' MRO pour la méthode suivante, pour le droit de BC1.
  7. Rincer laver répétez jusqu'à ce que toutes les méthodes sont trouvé et l'a appelé.

Explication pour ton exemple

 MRO: D,B,C,A,object
  1. super(D, self).__init__() est appelé. isinstance(self, D) => Vrai
  2. Recherche de méthode suivante dans le MRO dans les classes à droite de D.

    B.__init__ trouvé et l'a appelé

  3. B.__init__ des appels super(B, self).__init__().

    isinstance(self, B) => Faux
    isinstance(self, D) => Vrai

  4. Ainsi, le MRO est le même, mais la recherche se poursuit à droite de B à d. C,A,font l'objet de recherches, un par un. Le prochain __init__ trouvé est appelé.

  5. Et ainsi de suite et ainsi de suite.

Une explication de super
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
Les choses à surveiller lors de l'utilisation de super
http://fuhm.net/super-harmful/
Pythons MRO Algorithme:
http://www.python.org/download/releases/2.3/mro/
super de docs:
http://docs.python.org/library/functions.html
Le bas de cette page a une belle section sur super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html

J'espère que cela aide à clarifier les choses.

6voto

Javier Points 33134

juste deviner:

self dans les quatre méthodes se rapportent au même objet, c'est-à-dire de la classe D . ainsi, en B.__init__() , l'appel à super(B,self) connaît l'ascendance entière du diamant de self et il doit extraire la méthode 'après' B . dans ce cas, c'est la classe C .

3voto

hrr Points 634

super() connaît le plein hiérarchie de classe. C'est ce qui se passe à l'intérieur de B init:

>>> super(B, self)
<super: <class 'B'>, <D object>>

Cela résout la question centrale,

comment fait l'objet renvoyé par super en D init définition de communiquer l'existence de C de l'objet renvoyé par super en B init définition?

À savoir, dans le B de l'init définition, self est une instance de l' D, et communique donc l'existence d' C. Par exemple C peuvent être trouvés en type(self).__mro__.

0voto

ajm Points 10000

EDIT: la Lecture de Jacob Gabrielson lien à Super Nocif a exposé ma Java arrière-plan - super() certainement se comporte différemment en Python. Je vais laisser le reste de ma réponse pour la référence ci-dessous:

Poster une réponse, parce que c'est trop gros pour un commentaire:

Maintenant que je la regarde à nouveau, et garder à l'esprit je n'ai pas les services d'un interprète à portée de main, ne serait-il pas s'attendre à voir:

D init
B init
C init
A init

de toute façon, parce qu' C sera appelant super() quel que soit?

En tout cas, je crois que le comportement peut probablement être expliqué par le fait que chacun doit être conscient que D hérite de deux B et C, dans cet ordre.

C'est une bonne question, et j'espère que quelqu'un peut vous donner une réponse définitive!

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