1190 votes

Comment fonctionne le Python ' s super() travail avec l’héritage multiple ?

Je suis assez nouveau en Python de la programmation orientée objet et j'ai du mal la compréhension de l' super() de la fonction (nouveau style de classes), en particulier quand il s'agit de 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 est la suivante: l' Third() classe d'hériter à la fois les méthodes constructeur? Si oui, alors qui sera exécuté avec super() et pourquoi?

Et si vous souhaitez exécuter l'autre? Je sais qu'il a quelque chose à faire avec Python méthode de résolution de l'ordre (MRO).

858voto

rbp Points 8956

Ceci est détaillé avec une quantité raisonnable de détail par Guido lui-même à http://python-history.blogspot.com/2010/06/method-resolution-order.html (y compris les deux précédentes tentatives).

Mais, brièvement: dans votre exemple, le Tiers() appellera First.__init__. Pour de telles situations simples, Python va chercher l'attribut (dans ce cas, __init__) sur la classe de ses parents, de gauche à droite. Donc, si vous définissez

class Third(First, Second):
    ...

Python look au Premier abord, et, si le Premier n'a pas l'attribut, en Seconde.

Cette situation devient plus complexe lorsque l'héritage commence les chemins de passage (par exemple, si le Premier a hérité de la Deuxième, par exemple). Lire le lien ci-dessus pour plus de détails, mais, en bref, Python va essayer de maintenir l'ordre dans lequel chaque classe apparaît sur la liste d'héritage, les enfants des classes de première.

Ainsi, par exemple, si vous avez eu:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

le MRO [Quatrième, Deuxième, Troisième, Première].

En passant: si Python ne peut pas trouver une méthode cohérente de résolution de l'ordre, il faudra lever une exception, au lieu de revenir à un comportement qui peut surprendre l'utilisateur.

Édité à l'ajout de l'exemple de l'ambiguïté d'un MRO:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

Devrait Troisième du MRO [Premier, Deuxième] ou [Deuxième, pour la Première fois? Il n'y a pas de façon évidente de l'attente, et Python génère une erreur:

TypeError: Erreur lors de l'appel de la métaclasse bases Ne peut pas créer une méthode de résolution de l'ordre (MRO) pour les bases Seconde, Première

329voto

lifeless Points 507

Votre code et les autres réponses, sont tous buggy. Ils manquent les appels super() dans les deux premières classes qui sont requises pour la coopératif sous-classement travailler.

Voici une version corrigée du code :

L’appel de super() recherche le /next méthode / dans la MRO à chaque étape, c’est pourquoi la première et la deuxième doivent l’avoir trop, sinon l’exécution s’arrête à la fin de `` .

C’est ce que j’obtiens :

61voto

monoceres Points 1707

Ceci est connu comme le Problème du diamant, la page a une entrée sur Python, mais bref, Python appelle les méthodes de la superclasse de gauche à droite.

36voto

brent.payne Points 1125

C'est comment j'ai résolu de question d'avoir l'héritage multiple avec différentes variables pour l'initialisation et le fait d'avoir plusieurs Mixin avec le même appel de fonction. J'ai dû ajouter explicitement les variables au passé de **kwargs et ajouter un MixIn interface à un point de terminaison pour super des appels.

Ici, A est extensible de la classe de base et de l' B et C sont MixIn les deux classes qui fournissent la fonction f. A et B s'attendent paramètre v leur __init__ et C prévoit w. La fonction f prend un paramètre y. Q hérite de l'ensemble des trois classes. MixInF est le mixin interface pour B et C.


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)

16voto

Trilarion Points 2045

Un autre ne sont pas encore couverts point de passage de paramètres pour l'initialisation des classes. Depuis la destination de l' super dépend de la sous-classe la seule bonne façon de passer les paramètres d'emballage tous ensemble. Alors attention à ne pas avoir le même nom de paramètre avec des significations différentes.

Exemple:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

donne:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

L'appel de la super-classe __init__ directement à l'affectation directe des paramètres est tentant, mais échoue si il n'y a aucun super appel dans une super-classe et/ou le MRO a changé et qu'Une classe peut être appelée plusieurs fois, en fonction de la mise en œuvre.

Pour conclure: coopérative d'héritage et super et les paramètres spécifiques pour l'initialisation ne fonctionnent pas très bien ensemble.

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