Quelle est la différence entre:
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
et:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
J'ai vu super
beaucoup utilisé dans les classes avec un seul héritage. Je peux voir pourquoi vous l'utiliseriez en héritage multiple mais je ne sais pas quels sont les avantages de l'utiliser dans ce genre de situation.
la source
Quelle est la différence?
signifie appeler
SomeBaseClass
's__init__
. tandis quesignifie appeler une borne
__init__
de la classe parent qui suitChild
dans l'ordre de résolution de méthode (MRO) de l'instance.Si l'instance est une sous-classe de Child, il peut y avoir un parent différent qui vient ensuite dans le MRO.
Expliqué simplement
Lorsque vous écrivez une classe, vous souhaitez que d'autres classes puissent l'utiliser.
super()
permet aux autres classes d'utiliser plus facilement la classe que vous écrivez.Comme le dit Bob Martin, une bonne architecture vous permet de reporter la prise de décision le plus longtemps possible.
super()
peut permettre ce type d'architecture.Lorsqu'une autre classe sous-classe la classe que vous avez écrite, elle peut également hériter d'autres classes. Et ces classes pourraient avoir un
__init__
qui vient après cela en__init__
fonction de l'ordre des classes pour la résolution de la méthode.Sans cela,
super
vous coderiez probablement en dur le parent de la classe que vous écrivez (comme le fait l'exemple). Cela signifierait que vous n'appeleriez pas le suivant__init__
dans le MRO et que vous ne pourriez donc pas réutiliser le code qu'il contient.Si vous écrivez votre propre code pour un usage personnel, cette distinction peut ne pas vous intéresser. Mais si vous voulez que d'autres utilisent votre code, l'utilisation
super
est une chose qui permet une plus grande flexibilité pour les utilisateurs du code.Python 2 contre 3
Cela fonctionne en Python 2 et 3:
Cela ne fonctionne que dans Python 3:
Il fonctionne sans argument en remontant dans le cadre de la pile et en obtenant le premier argument de la méthode (généralement
self
pour une méthode d'instance oucls
pour une méthode de classe - mais pourrait être d'autres noms) et en trouvant la classe (par exempleChild
) dans les variables libres ( il est recherché avec le nom__class__
comme variable de fermeture libre dans la méthode).Je préfère démontrer la manière croisée d'utiliser
super
, mais si vous utilisez uniquement Python 3, vous pouvez l'appeler sans arguments.Indirection avec compatibilité ascendante
Qu'est-ce que ça vous donne? Pour l'héritage unique, les exemples de la question sont pratiquement identiques du point de vue de l'analyse statique. Cependant, l'utilisation
super
vous donne une couche d'indirection avec compatibilité ascendante.La compatibilité ascendante est très importante pour les développeurs chevronnés. Vous voulez que votre code continue de fonctionner avec des modifications minimales au fur et à mesure que vous le modifiez. Lorsque vous regardez votre historique de révision, vous voulez voir précisément ce qui a changé quand.
Vous pouvez commencer avec un héritage unique, mais si vous décidez d'ajouter une autre classe de base, vous n'avez qu'à changer la ligne avec les bases - si les bases changent dans une classe dont vous héritez (disons qu'un mixin est ajouté) vous changeriez rien dans cette classe. En particulier dans Python 2, obtenir les arguments
super
et les arguments de méthode corrects peut être difficile. Si vous savez que vous utilisezsuper
correctement avec l'héritage unique, cela rend le débogage moins difficile à l'avenir.Injection de dépendance
D'autres personnes peuvent utiliser votre code et injecter des parents dans la résolution de la méthode:
Supposons que vous ajoutiez une autre classe à votre objet et que vous souhaitiez injecter une classe entre Foo et Bar (à des fins de test ou pour toute autre raison):
L'utilisation de l'enfant non super ne parvient pas à injecter la dépendance car l'enfant que vous utilisez a codé en dur la méthode à appeler après la sienne:
Cependant, la classe avec l'enfant qui utilise
super
peut injecter correctement la dépendance:Adresser un commentaire
Python linéarise un arbre d'héritage compliqué via l' algorithme de linéarisation C3 pour créer un ordre de résolution de méthode (MRO).
Nous voulons que les méthodes soient recherchées dans cet ordre .
Pour qu'une méthode définie dans un parent trouve la suivante dans cet ordre sans
super
, il faudraitLe
UnsuperChild
n'a pas accès àInjectMe
. C'est la méthodeUnsuperInjector
qui a accès àInjectMe
la méthode dont elle hérite et qui ne peut pas appeler la méthode de cette classeUnsuperChild
.Les deux classes enfants ont l'intention d'appeler une méthode du même nom qui vient ensuite dans le MRO, qui pourrait être une autre classe dont elle n'était pas au courant lorsqu'elle a été créée.
Celui qui ne
super
code pas en dur la méthode de son parent - a donc restreint le comportement de sa méthode, et les sous-classes ne peuvent pas injecter de fonctionnalité dans la chaîne d'appel.Celui qui
super
a une plus grande flexibilité. La chaîne d'appel pour les méthodes peut être interceptée et la fonctionnalité injectée.Vous n'avez peut-être pas besoin de cette fonctionnalité, mais les sous-classes de votre code peuvent l'être.
Conclusion
Utilisez toujours
super
pour référencer la classe parente au lieu de la coder en dur.Ce que vous avez l'intention de faire, c'est de référencer la classe parente qui est la suivante, pas spécifiquement celle dont vous voyez l'enfant hérité.
Ne pas utiliser
super
peut imposer des contraintes inutiles aux utilisateurs de votre code.la source
list
interface, disons quedoublylinkedlist
l'application la sélectionne en douceur. Je peux rendre mon exemple plus configurable en introduisantconfig.txt
et en reliant l'implémentation au moment du chargement. Est-ce le bon exemple? Si oui, comment associer votre code? Voir le premier adv de DI sur wiki. Où une nouvelle implémentation est-elle configurable? dans votre codeInjectMe
classe. Les commentaires ne sont pas pour la discussion, cependant, je vous suggère de discuter de cela plus en détail avec d'autres dans le chat ou de poser une nouvelle question sur le site principal.__init__
fonctions. surtout si la signature de__init__
varie selon les classes dans la hiérarchie. J'ai ajouté une réponse qui se concentre sur cet aspectJ'avais joué un peu avec
super()
et j'avais reconnu que nous pouvions changer l'ordre d'appel.Par exemple, nous avons la structure hiérarchique suivante:
Dans ce cas, MRO de D sera (uniquement pour Python 3):
Créons une classe où les
super()
appels après l'exécution de la méthode.Nous pouvons donc voir que l'ordre de résolution est le même que dans MRO. Mais quand on appelle
super()
au début de la méthode:Nous avons un ordre différent, il est inversé un ordre du tuple MRO.
Pour une lecture supplémentaire, je recommanderais les réponses suivantes:
la source
Tout cela ne suppose-t-il pas que la classe de base est une classe de nouveau style?
Ne fonctionnera pas en Python 2.
class A
doit être de nouveau style, c'est-à-dire:class A(object)
la source
Lorsque vous appelez
super()
pour résoudre la version d'un parent d'une méthode de classe, d'une méthode d'instance ou d'une méthode statique, nous voulons passer la classe actuelle dont la portée est dans le premier argument, pour indiquer la portée du parent que nous essayons de résoudre et en tant que un deuxième argument l'objet d'intérêt pour indiquer à quel objet nous essayons d'appliquer cette portée.Considérons une hiérarchie de classes
A
,B
etC
où chaque classe est le parent de celui qui le suit, eta
,b
etc
instances de chaque respectives.Utilisation
super
avec une méthode statiquepar exemple en utilisant
super()
de l'intérieur de la__new__()
méthodeExplication:
1- même s'il est habituel
__new__()
de prendre comme premier paramètre une référence à la classe appelante, il n'est pas implémenté en Python comme méthode de classe, mais plutôt comme méthode statique. Autrement dit, une référence à une classe doit être passée explicitement comme premier argument lors de l'appel__new__()
direct:2- lors de l'appel
super()
pour accéder à la classe parent, nous passons la classe enfantA
comme premier argument, puis nous passons une référence à l'objet d'intérêt, dans ce cas, c'est la référence de classe qui a été transmise lors de l'A.__new__(cls)
appel. Dans la plupart des cas, il s'agit également d'une référence à la classe enfant. Dans certaines situations, cela peut ne pas être le cas, par exemple dans le cas d'héritages de plusieurs générations.3- puisque comme une règle générale
__new__()
est une méthode statique,super(A, cls).__new__
retourne également une méthode statique et doit être fourni explicitement tous les arguments, y compris la référence à l'objet de insterest, dans ce cascls
.4- Faire la même chose sans
super
Utilisation
super
avec une méthode d'instancepar exemple en utilisant
super()
de l'intérieur__init__()
Explication:
1-
__init__
est une méthode d'instance, ce qui signifie qu'elle prend comme premier argument une référence à une instance. Lorsqu'elle est appelée directement à partir de l'instance, la référence est transmise implicitement, c'est-à-dire que vous n'avez pas besoin de la spécifier:2- lors de l'appel à l'
super()
intérieur,__init__()
nous passons la classe enfant comme premier argument et l'objet d'intérêt comme deuxième argument, qui en général est une référence à une instance de la classe enfant.3- L'appel
super(A, self)
renvoie un proxy qui résoudra la portée et l'appliqueraself
comme s'il s'agissait désormais d'une instance de la classe parente. Appelons ce proxys
. Puisque__init__()
c'est une méthode d'instance, l'appels.__init__(...)
passera implicitement une référence deself
comme premier argument à celle du parent__init__()
.4- pour faire de même sans
super
avoir besoin de passer explicitement une référence à une instance à la version parent de__init__()
.Utilisation
super
avec une méthode de classeExplication:
1- Une méthode de classe peut être appelée directement depuis la classe et prend comme premier paramètre une référence à la classe.
2- Lorsque nous appelons
super()
dans une méthode de classe pour résoudre la version de son parent, nous voulons passer la classe enfant actuelle comme premier argument pour indiquer la portée du parent que nous essayons de résoudre et l'objet d'intérêt comme deuxième argument pour indiquer à quel objet nous voulons appliquer cette portée, qui est en général une référence à la classe enfant elle-même ou à l'une de ses sous-classes.3- L'appel
super(B, cls)
résout le champ d'applicationA
et l'appliquecls
. Puisquealternate_constructor()
c'est une méthode de classe, l'appelsuper(B, cls).alternate_constructor(...)
passera implicitement une référence decls
comme premier argument àA
la version dealternate_constructor()
4- Pour faire de même sans utiliser,
super()
vous devez obtenir une référence à la version non liée deA.alternate_constructor()
(c'est-à-dire la version explicite de la fonction). Faire simplement cela ne fonctionnerait pas:Ce qui précède ne fonctionnerait pas car la
A.alternate_constructor()
méthode prend une référence implicite àA
comme premier argument. L'cls
être passé ici serait son deuxième argument.la source
Beaucoup de bonnes réponses, mais pour les apprenants visuels: permet d'abord d'explorer avec des arguments à super, puis sans.
Imaginez qu'il existe une instance
jack
créée à partir de la classeJack
, qui possède la chaîne d'héritage comme indiqué en vert dans l'image. Appel:super(Jack, jack).method(...)
utilisera le MRO (Method Resolution Order) de
jack
(son arbre d'héritage dans un certain ordre), et commencera la recherche à partir deJack
. Pourquoi peut-on fournir une classe parent? Eh bien, si nous commençons à chercher à partir de l'instancejack
, il trouverait la méthode d'instance, le but est de trouver sa méthode parente.Si l'on ne fournit pas d'arguments à super, c'est comme le premier argument passé est la classe de
self
, et le deuxième argument passé estself
. Ceux-ci sont calculés automatiquement pour vous en Python3.Cependant, disons que nous ne voulons pas utiliser
Jack
la méthode de, au lieu de passerJack
, nous pourrions passerJen
pour commencer à chercher la méthode à partir deJen
.Il recherche une couche à la fois (largeur pas de profondeur), par exemple si
Adam
etSue
tous les deux ont la méthode désirée, celle deSue
se trouver en premier.Si
Cain
et lesSue
deux avaient la méthode requise,Cain
la méthode de serait appelée en premier. Cela correspond en code à:MRO est de gauche à droite.
la source
quelques bonnes réponses ici, mais elles n'abordent pas comment utiliser
super()
dans le cas où différentes classes de la hiérarchie ont des signatures différentes ... en particulier dans le cas de__init__
pour répondre à cette partie et être en mesure de l'utiliser efficacement,
super()
je suggère de lire ma réponse super () et de changer la signature des méthodes coopératives .voici juste la solution à ce scénario:
exemple d'utilisation:
production:
la source
C'est assez facile à comprendre.
Ok, que se passe-t-il maintenant si vous utilisez
super(Child,self)
?Lorsqu'une instance enfant est créée, son MRO (Method Resolution Order) est dans l'ordre de (Child, SomeBaseClass, object) en fonction de l'héritage. (supposez que SomeBaseClass n'a pas d'autres parents à l'exception de l'objet par défaut)
En passant
Child, self
,super
recherche dans le MRO de l'self
instance et retourne l'objet proxy à côté de Child, dans ce cas c'est SomeBaseClass, cet objet invoque alors la__init__
méthode de SomeBaseClass. En d'autres termes, si c'est le cassuper(SomeBaseClass,self)
, l'objet proxy quisuper
retourne seraitobject
Pour l'héritage multiple, le MRO peut contenir de nombreuses classes,
super
vous permet donc de décider où vous souhaitez commencer la recherche dans le MRO.la source
Considérez le code suivant:
Si vous changez de constructeur
Y
enX.__init__
, vous obtiendrez:Mais en utilisant
super(Y, self).__init__()
, vous obtiendrez:Et
P
ouQ
peut même être impliqué à partir d'un autre fichier que vous ne connaissez pas lorsque vous écrivezX
etY
. Donc, fondamentalement, vous ne saurez pas à quoisuper(Child, self)
fera référence lorsque vous écrivezclass Y(X)
, même la signature de Y est aussi simple queY(X)
. C'est pourquoi super pourrait être un meilleur choix.la source