Je suis à peu près nouveau dans la programmation orientée objet Python et j'ai du mal à comprendre la super()
fonction (nouvelles classes de style), surtout en ce qui concerne l'héritage multiple.
Par exemple, si vous avez quelque chose comme:
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
Ce que je ne comprends pas, c'est: la Third()
classe héritera-t-elle des deux méthodes constructeurs? Si oui, alors lequel sera exécuté avec super () et pourquoi?
Et si vous voulez exécuter l'autre? Je sais que cela a quelque chose à voir avec l'ordre de résolution de la méthode Python ( MRO ).
python
multiple-inheritance
Callisto
la source
la source
super()
est utile. Je ne recommanderais pas de l'utiliser avec des classes utilisant l'héritage linéaire, où c'est juste une surcharge inutile.super()
, c'est qu'il oblige chaque sous-classe à l'utiliser également, tandis que lorsqu'il ne l'utilise passuper()
, tout le monde sous-classe peut décider lui-même. Si un développeur l'utilisant ne sait passuper()
ou ne sait pas qu'il a été utilisé, des problèmes avec la macro peuvent survenir et sont très difficiles à localiser.Réponses:
Ceci est détaillé avec une quantité raisonnable de détails par Guido lui-même dans son article de blog Method Resolution Order (y compris deux tentatives antérieures).
Dans votre exemple,
Third()
va appelerFirst.__init__
. Python recherche chaque attribut dans les parents de la classe car ils sont répertoriés de gauche à droite. Dans ce cas, nous recherchons__init__
. Donc, si vous définissezPython commencera par regarder
First
, et, s'ilFirst
n'a pas l'attribut, alors il regarderaSecond
.Cette situation devient plus complexe lorsque l'héritage commence à croiser les chemins (par exemple s'il est
First
hérité deSecond
). Lisez le lien ci-dessus pour plus de détails, mais, en un mot, Python essaiera de maintenir l'ordre dans lequel chaque classe apparaît dans la liste d'héritage, en commençant par la classe enfant elle-même.Ainsi, par exemple, si vous aviez:
le MRO serait
[Fourth, Second, Third, First].
Soit dit en passant: si Python ne peut pas trouver un ordre de résolution de méthode cohérent, il lèvera une exception, au lieu de retomber sur un comportement qui pourrait surprendre l'utilisateur.
Modifié pour ajouter un exemple de MRO ambigu:
Devrait
Third
être MRO »[First, Second]
ou[Second, First]
? Il n'y a aucune attente évidente et Python générera une erreur:Edit: Je vois plusieurs personnes affirmer que les exemples ci-dessus manquent d'
super()
appels, alors laissez-moi vous expliquer: le but des exemples est de montrer comment le MRO est construit. Ils ne sont pas destinés à imprimer "premier \ nsecond \ tiers" ou autre. Vous pouvez - et devriez, bien sûr, jouer avec l'exemple, ajouter dessuper()
appels, voir ce qui se passe et acquérir une compréhension plus approfondie du modèle d'héritage de Python. Mais mon objectif ici est de rester simple et de montrer comment le MRO est construit. Et il est construit comme je l'ai expliqué:la source
Votre code et les autres réponses sont tous bogués. Ils manquent les
super()
appels dans les deux premières classes qui sont nécessaires pour que le sous-classement coopératif fonctionne.Voici une version fixe du code:
L'
super()
appel trouve la méthode suivante dans le MRO à chaque étape, c'est pourquoi First et Second doivent l'avoir aussi, sinon l'exécution s'arrête à la fin deSecond.__init__()
.Voici ce que j'obtiens:
la source
super
elles ne fonctionneront pas (en raison de la non-concordance des paramètres), ou elles n'appelleront pas peu de bases (parce que vous n'avez pas écritsuper
dans l'une des bases qui rompt le lien)!Je voulais élaborer un peu la réponse sans vie parce que quand j'ai commencé à lire comment utiliser super () dans une hiérarchie d'héritage multiple en Python, je ne l'ai pas obtenue immédiatement.
Ce que vous devez comprendre, c'est qu'il
super(MyClass, self).__init__()
fournit la méthode suivante en__init__
fonction de l'algorithme MRO (Method Resolution Ordering) utilisé dans le contexte de la hiérarchie d'héritage complète .Cette dernière partie est cruciale à comprendre. Reprenons l'exemple:
Selon cet article sur Guido van Rossum sur l'ordre de résolution des méthodes , l'ordre à résoudre
__init__
est calculé (avant Python 2.3) à l'aide d'un "parcours de profondeur en premier de gauche à droite":Après avoir supprimé tous les doublons, à l'exception du dernier, nous obtenons:
Ainsi, suivons ce qui se passe lorsque nous instancions une instance de la
Third
classe, par exemplex = Third()
.Third.__init__
s'exécute.Third(): entering
super(Third, self).__init__()
s'exécute et MRO retourneFirst.__init__
qui est appelé.First.__init__
s'exécute.First(): entering
super(First, self).__init__()
s'exécute et MRO retourneSecond.__init__
qui est appelé.Second.__init__
s'exécute.Second(): entering
super(Second, self).__init__()
s'exécute et MRO retourneobject.__init__
qui est appelé.object.__init__
s'exécute (pas d'instructions d'impression dans le code)Second.__init__
laquelle imprime ensuiteSecond(): exiting
First.__init__
laquelle imprime ensuiteFirst(): exiting
Third.__init__
laquelle imprime ensuiteThird(): exiting
Cela explique pourquoi l'instanciation de Third () entraîne:
L'algorithme MRO a été amélioré à partir de Python 2.3 pour bien fonctionner dans les cas complexes, mais je suppose que l'utilisation de la "traversée de profondeur en premier de gauche à droite" + "en supprimant les doublons attendus pour le dernier" fonctionne toujours dans la plupart des cas (veuillez si ce n'est pas le cas). N'oubliez pas de lire le blog de Guido!
la source
Third
n'a pas hérité deSecond
, puissuper(First, self).__init__
appelerobject.__init__
et après le retour, "premier" serait imprimé. Mais parce queThird
hérite des deuxFirst
etSecond
, plutôt que d'appelerobject.__init__
aprèsFirst.__init__
le MRO, seul l'appel finalobject.__init__
est conservé et les instructions printFirst
etSecond
ne sont pas atteintes avant leobject.__init__
retour. Depuis qu'il aSecond
été le dernier à appelerobject.__init__
, il revient à l'intérieurSecond
avant de rentrerFirst
.List[subclass]
comme unList[superclass]
sisubclass
est une sous-classe desuperclass
(List
vient dutyping
module de PEP 483 iirc).Ceci est connu sous le nom de problème Diamond , la page a une entrée sur Python, mais en bref, Python appellera les méthodes de la superclasse de gauche à droite.
la source
object
est le quatrièmeVoici comment j'ai résolu le problème d'avoir plusieurs héritages avec différentes variables pour l'initialisation et d'avoir plusieurs MixIns avec le même appel de fonction. J'ai dû ajouter explicitement des variables aux kwargs ** passés et ajouter une interface MixIn pour être un point de terminaison pour les super appels.
Voici
A
une classe de base extensibleB
et ceC
sont des classes MixIn qui fournissent toutes deux une fonctionf
.A
et lesB
deux attendent un paramètrev
dans leur__init__
etC
attendw
. La fonctionf
prend un paramètrey
.Q
hérite des trois classes.MixInF
est l'interface de mixage pourB
etC
.la source
args
/kwargs
plutôt que sur des paramètres nommés.Je comprends que cela ne répond pas directement à la
super()
question, mais je pense que c'est suffisamment pertinent pour être partagé.Il existe également un moyen d'appeler directement chaque classe héritée:
Il suffit de noter que si vous le faites de cette façon, vous devrez appeler chaque manuellement comme je suis assez sûr
First
« s__init__()
ne sera pas appelé.la source
First
etSecond
héritent tous les deux d'une autre classe et l'appellent directement, cette classe commune (point de départ du losange) est appelée deux fois. super évite cela.object
être appelé deux fois. Je n'y ai pas pensé. Je voulais juste souligner que vous appelez directement les classes parentes.Global
En supposant que tout découle de
object
(vous êtes seul si ce n'est pas le cas), Python calcule un ordre de résolution de méthode (MRO) basé sur votre arbre d'héritage de classe. Le MRO satisfait 3 propriétés:Si aucun ordre de ce type n'existe, des erreurs Python. Le fonctionnement interne de ceci est une linéarisation C3 de l'ascendance des classes. Lisez tout à ce sujet ici: https://www.python.org/download/releases/2.3/mro/
Ainsi, dans les deux exemples ci-dessous, c'est:
Lorsqu'une méthode est appelée, la première occurrence de cette méthode dans le MRO est celle qui est appelée. Toute classe qui n'implémente pas cette méthode est ignorée. Tout appel à l'
super
intérieur de cette méthode appellera la prochaine occurrence de cette méthode dans le MRO. Par conséquent, il importe à la fois dans quel ordre vous placez les classes en héritage et où vous placez les appels àsuper
dans les méthodes.Avec le
super
premier dans chaque méthodeChild()
Les sorties:Avec le
super
dernier dans chaque méthodeChild()
Les sorties:la source
Left
aidesuper()
deChild
. supposons que je veuille y accéderRight
de l'intérieurChild
. Existe-t-il un moyen d'accéderRight
àChild
partir de Super? Ou dois-je appeler directementRight
de l'intérieursuper
?À propos du commentaire de @ calfzhou , vous pouvez utiliser, comme d'habitude,
**kwargs
:Exemple de course en ligne
Résultat:
Vous pouvez également les utiliser de manière positionnelle:
mais vous devez vous rappeler le MRO, c'est vraiment déroutant.
Je peux être un peu ennuyeux, mais j'ai remarqué que les gens oublient à chaque fois d'utiliser
*args
et**kwargs
quand ils remplacent une méthode, alors que c'est l'une des rares utilisations vraiment utiles et sensées de ces `` variables magiques ''.la source
Un autre point non encore couvert est le passage de paramètres pour l'initialisation des classes. Depuis la destination de
super
dépend de la sous-classe, le seul bon moyen de passer les paramètres est de les regrouper tous. Veillez ensuite à ne pas avoir le même nom de paramètre avec des significations différentes.Exemple:
donne:
Appeler
__init__
directement la super classe pour une affectation plus directe des paramètres est tentant mais échoue s'il y en asuper
appel dans une super classe et / ou le MRO est changé et la classe A peut être appelée plusieurs fois, selon l'implémentation.Pour conclure: l'héritage coopératif et les paramètres super et spécifiques pour l'initialisation ne fonctionnent pas très bien ensemble.
la source
La sortie est
L'appel à Third () localise l' init défini dans Third. Et appelez super dans cette routine invoque init défini dans First. MRO = [Premier, deuxième]. Maintenant, l'appel à super dans init défini dans First continuera la recherche MRO et trouvera init défini dans Second, et tout appel à super frappera l'objet init par défaut . J'espère que cet exemple clarifie le concept.
Si vous n'appelez pas super de First. La chaîne s'arrête et vous obtiendrez la sortie suivante.
la source
Dans learningpythonthehardway j'apprends quelque chose appelé super () une fonction intégrée si elle ne se trompe pas. L'appel de la fonction super () peut aider l'héritage à passer par le parent et les «frères et sœurs» et vous aider à voir plus clairement. Je suis encore débutant mais j'aime partager mon expérience sur l'utilisation de ce super () en python2.7.
Si vous avez lu les commentaires de cette page, vous entendrez parler de l'ordre de résolution de méthode (MRO), la méthode étant la fonction que vous avez écrite, MRO utilisera le schéma Profondeur en premier de gauche à droite pour rechercher et exécuter. Vous pouvez faire plus de recherches à ce sujet.
En ajoutant la fonction super ()
Vous pouvez connecter plusieurs instances et «familles» avec super (), en ajoutant chacune d'elles. Et il exécutera les méthodes, les passera en revue et s'assurera que vous ne les avez pas ratées! Cependant, les ajouter avant ou après fait une différence, vous saurez si vous avez fait l'exercice d'apprentissage 44. Laissez le plaisir commencer !!
En prenant l'exemple ci-dessous, vous pouvez copier et coller et essayer de l'exécuter:
Comment ça marche? L'instance de cinquième () va comme ceci. Chaque étape va de classe en classe où la super fonction a été ajoutée.
Le parent a été retrouvé et il ira continuer aux troisième et quatrième !!
Maintenant, toutes les classes avec super () sont accessibles! La classe parente a été trouvée et exécutée et maintenant elle continue de décompresser la fonction dans les héritages pour terminer les codes.
Le résultat du programme ci-dessus:
Pour moi, l'ajout de super () me permet de voir plus clairement comment python exécuterait mon codage et de m'assurer que l'héritage peut accéder à la méthode que je voulais.
la source
Je voudrais ajouter à ce que dit @Visionscaper en haut:
Dans ce cas, l'interpréteur ne filtre pas la classe d'objets parce qu'elle est dupliquée, mais plutôt parce que Second apparaît dans une position de tête et n'apparaît pas dans la position de queue dans un sous-ensemble de hiérarchie. Alors que l'objet n'apparaît que dans les positions de queue et n'est pas considéré comme une position forte dans l'algorithme C3 pour déterminer la priorité.
La linéarisation (mro) d'une classe C, L (C), est la
La fusion linéarisée se fait en sélectionnant les classes communes qui apparaissent en tête de liste et non en queue car l'ordre compte (cela deviendra clair ci-dessous)
La linéarisation de Third peut être calculée comme suit:
Ainsi pour une implémentation super () dans le code suivant:
il devient évident comment cette méthode sera résolue
la source
En python 3.5+, l'héritage semble prévisible et très agréable pour moi. Veuillez regarder ce code:
Les sorties:
Comme vous pouvez le voir, il appelle foo exactement UNE fois pour chaque chaîne héritée dans le même ordre qu'il a été hérité. Vous pouvez obtenir cette commande en appelant . mro :
Quatrième -> Troisième -> Premier -> Deuxième -> Base -> objet
la source
Peut-être qu'il y a encore quelque chose à ajouter, un petit exemple avec Django rest_framework et des décorateurs. Cela fournit une réponse à la question implicite: "pourquoi voudrais-je de toute façon?"
Comme dit: nous sommes avec Django rest_framework, et nous utilisons des vues génériques, et pour chaque type d'objets dans notre base de données, nous nous retrouvons avec une classe de vue fournissant GET et POST pour les listes d'objets, et une autre classe de vue fournissant GET , PUT et DELETE pour les objets individuels.
Maintenant, le POST, le PUT et le DELETE que nous voulons décorer avec le login_required de Django. Remarquez comment cela touche les deux classes, mais pas toutes les méthodes des deux classes.
Une solution pourrait passer par l'héritage multiple.
De même pour les autres méthodes.
Dans la liste d'héritage de mes classes concrètes, j'ajouterais mon
LoginToPost
avantListCreateAPIView
etLoginToPutOrDelete
avantRetrieveUpdateDestroyAPIView
. Mes cours concretsget
resteraient sans décoration.la source
Publier cette réponse pour ma future référence.
L'héritage multiple Python doit utiliser un modèle en losange et la signature de la fonction ne doit pas changer dans le modèle.
L'exemple d'extrait de code serait: -
Ici, la classe A est
object
la source
A
devrait également appeler__init__
.A
n'a pas "inventé" la méthode__init__
, elle ne peut donc pas supposer qu'une autre classe peut avoir été utiliséeA
plus tôt dans son MRO. La seule classe dont la__init__
méthode n'appelle pas (et ne devrait pas) appelersuper().__init__
estobject
.object
peut - être je pense, je devrais écrire à laclass A (object) :
placeA
ne peut pas l'êtreobject
si vous ajoutez un paramètre à son__init__
.