J'exécute Python 2.5, donc cette question peut ne pas s'appliquer à Python 3. Lorsque vous créez une hiérarchie de classe Diamond en utilisant l'héritage multiple et créez un objet de la classe la plus dérivée, Python fait la bonne chose (TM). Il appelle le constructeur de la classe la plus dérivée, puis ses classes parentes comme indiqué de gauche à droite, puis le grand-parent. Je connais Python MRO ; ce n'est pas ma question. Je suis curieux de voir comment l'objet renvoyé de super parvient à communiquer avec les appels de super dans le classes parent l'ordre correct. Considérez 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 fait la chose intuitive, il imprime:
D init
B init
C init
A init
Cependant, si vous commentez l'appel à super dans la fonction init de B, ni la fonction init de A ni de C n'est appelée. Cela signifie que l'appel de B à super est en quelque sorte conscient de l'existence de C dans la hiérarchie globale des classes. Je sais que super renvoie un objet proxy avec un opérateur get surchargé, mais comment l'objet renvoyé par super dans la définition init de D communique-t-il l'existence de C à l'objet renvoyé par super dans la définition init de B? Les informations que les appels ultérieurs de super utilisation sont-elles stockées sur l'objet lui-même? Si c'est le cas, pourquoi n'est-ce pas super au lieu de self.super?
Edit: Jekke a souligné à juste titre que ce n'est pas self.super parce que super est un attribut de la classe, pas une instance de la classe. Conceptuellement, cela a du sens, mais dans la pratique, le super n'est pas non plus un attribut de la classe! Vous pouvez tester cela dans l'interpréteur en créant deux classes A et B, où B hérite de A, et en appelant dir(B)
. Il n'a pas d'attributs super
ou __super__
.
J'ai fourni un tas de liens ci-dessous, qui répondent à votre question plus en détail et plus précisément que je ne peux l'espérer. Je répondrai cependant à votre question dans mes propres mots également, pour vous faire gagner du temps. Je vais le mettre en points -
__mro__
, Qui stocke l'ordre de résolution des méthodes de cette instance particulière.__init__
) dans les classes du MRO, après la classe spécifiée comme premier argument (dans ce cas, les classes après DC).Explication de votre exemple
MRO: D,B,C,A,object
super(D, self).__init__()
est appelée. isinstance (self, D) => VraiRecherchez la méthode suivante dans le MRO dans les classes à droite de D.
B.__init__
Trouvé et appelé
B.__init__
Appelle super(B, self).__init__()
.
isinstance (self, B) => Faux
isinstance (self, D) => True
Ainsi, le MRO est le même, mais la recherche continue à droite de B, c'est-à-dire que C, A, les objets sont recherchés un par un. Le prochain __init__
Trouvé est appelé.
Et ainsi de suite.
Une explication de super
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
Choses à surveiller lors de l'utilisation de super
http://fuhm.net/super-harmful/
Algorithme MRO Pythons:
http://www.python.org/download/releases/2.3/mro/
documents de super:
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.
Changez votre code en ceci et je pense que cela expliquera les choses (vraisemblablement super
regarde où, par exemple, B
est 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'>)
Cela vaut également la peine de vérifier Python's Super est astucieux, mais vous ne pouvez pas l'utiliser .
juste deviner:
self
dans les quatre méthodes fait référence au même objet, c'est-à-dire de la classe D
. donc, dans B.__init__()
, l'appel à super(B,self)
connaît toute l'ascendance diamant de self
et il doit récupérer la méthode de 'after' B
. dans ce cas, c'est la classe C
.
super()
connaît la hiérarchie de classe complète. C'est ce qui se passe à l'intérieur de l'init de B:
>>> super(B, self)
<super: <class 'B'>, <D object>>
Cela résout la question centrale,
comment l'objet renvoyé par super dans la définition init de D communique-t-il l'existence de C à l'objet renvoyé par super dans la définition init de B?
A savoir, dans la définition init de B, self
est une instance de D
, et communique ainsi l'existence de C
. Par exemple, C
se trouve dans type(self).__mro__
.
La réponse de Jacob montre comment comprendre le problème, tandis que celle de batbrat montre les détails et celle des RH va droit au but.
Une chose qu'ils ne couvrent pas (du moins pas explicitement) de votre question est ce point:
Cependant, si vous commentez l'appel à super dans la fonction init de B, ni la fonction init de A ni de C n'est appelée.
Pour comprendre cela, changez le code de Jacob pour imprimer la pile sur l'init de A, comme ci-dessous:
import traceback
class A(object):
def __init__(self):
print "A init"
print self.__class__.__mro__
traceback.print_stack()
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()
Il est un peu surprenant de voir que la ligne de B
super(B, self).__init__()
appelle en fait C.__init__()
, car C
n'est pas une classe de base de B
.
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'>)
File "/tmp/jacobs.py", line 31, in <module>
x = D()
File "/tmp/jacobs.py", line 29, in __init__
super(D, self).__init__()
File "/tmp/jacobs.py", line 17, in __init__
super(B, self).__init__()
File "/tmp/jacobs.py", line 23, in __init__
super(C, self).__init__()
File "/tmp/jacobs.py", line 11, in __init__
traceback.print_stack()
Cela se produit car super (B, self)
n'est pas 'appel de la version de base de B de __init__
'. Au lieu de cela, c'est 'appelant __init__
Sur la première classe à droite de B
qui est présent sur __mro__
De self
et qui a tel un attribut.
Donc, si vous commentez l'appel à super dans la fonction init de B, la pile de méthodes s'arrêtera sur B.__init__
, Et n'atteindra jamais C
ou A
.
Résumer:
self
est toujours une référence à l'instance, et ses __mro__
Et __class__
Restent constants__mro__
. Comme le __mro__
Reste constant, ce qui se passe, c'est qu'il est recherché comme une liste, pas comme un arbre ou un graphique.Sur ce dernier point, notez que le nom complet de l'algorithme du MRO est linéarisation C3 superclasse. Autrement dit, il aplatit cette structure en une liste. Lorsque les différents appels super()
se produisent, ils itèrent efficacement cette liste.