web-dev-qa-db-fra.com

python héritage multiple passant des arguments aux constructeurs utilisant super

Considérez l'extrait de code suivant python code

class A(object):
    def __init__(self, a):
        self.a = a

class B(A):
    def __init__(self, a, b):
        super(B, self).__init__(a)
        self.b = b

class C(A):
    def __init__(self, a, c):
        super(C, self).__init__(a)
        self.c = c

class D(B, C):
    def __init__(self, a, b, c, d):
        #super(D,self).__init__(a, b, c) ???
        self.d = d

Je me demande comment puis-je passer a, b et c aux constructeurs des classes de base correspondantes.

34
Lin

Eh bien, lorsqu'il s'agit d'héritage multiple en général, vos classes de base (malheureusement) devraient être conçues pour l'héritage multiple. Les classes B et C dans votre exemple ne le sont pas, et vous n'avez donc pas pu trouver un moyen approprié d'appliquer super dans D.

L'une des façons courantes de concevoir vos classes de base pour l'héritage multiple est que les classes de base de niveau intermédiaire acceptent des arguments supplémentaires dans leur __init__, qu'ils n'ont pas l'intention d'utiliser, et transmettez-les à leur appel super.

Voici une façon de le faire en python:

class A(object):
    def __init__(self,a):
        self.a=a

class B(A):
    def __init__(self,b,**kw):
        self.b=b
        super(B,self).__init__(**kw)

 class C(A):
    def __init__(self,c,**kw):
        self.c=c
        super(C,self).__init__(**kw)

class D(B,C):
    def __init__(self,a,b,c,d):
        super(D,self).__init__(a=a,b=b,c=c)
        self.d=d

Cela peut être considéré comme décevant, mais c'est comme ça.

46
shx2

Malheureusement, il n'y a aucun moyen de faire fonctionner cela en utilisant super() sans changer les classes de base. Tout appel aux constructeurs pour B ou C va essayer d'appeler la classe suivante dans Method Resolution Order , qui sera toujours B ou C au lieu de la classe A que les constructeurs B et C assument.

L'alternative consiste à appeler explicitement les constructeurs sans utiliser super() dans chaque classe.

class A(object):
    def __init__(self, a):
        object.__init__()
        self.a = a

class B(A):
    def __init__(self, a, b):
        A.__init__(self, a)
        self.b = b

class C(object):
    def __init__(self, a, c):
        A.__init__(self, a)
        self.c = c

class D(B, C):
    def __init__(self, a, b, c, d):
        B.__init__(self, a, b)
        C.__init__(self, a, c)
        self.d = d 

Il y a toujours un inconvénient ici car le constructeur A serait appelé deux fois, ce qui n'a pas vraiment beaucoup d'effet dans cet exemple, mais peut causer des problèmes dans les constructeurs plus complexes. Vous pouvez inclure une vérification pour empêcher le constructeur de s'exécuter plusieurs fois.

class A(object):
    def __init__(self, a):
        if hasattr(self, 'a'):
            return
        # Normal constructor.

Certains appelleraient cela une lacune de super(), et c'est dans un certain sens, mais c'est aussi juste une lacune dans l'héritage multiple en général. Les modèles d'héritage des diamants sont souvent sujets à des erreurs. Et beaucoup de solutions de contournement pour eux conduisent à un code encore plus confus et sujet aux erreurs. Parfois, la meilleure réponse est d'essayer de refactoriser votre code pour utiliser moins d'héritage multiple.

9
Brendan Abel