web-dev-qa-db-fra.com

Héritage multiple en python3 avec différentes signatures

J'ai trois classes: A, B et C.

C hérite de A et B (dans cet ordre). Les signatures de constructeur de A et B sont différentes. Comment puis-je appeler le __init__ méthodes des deux classes parentes?

Mon effort dans le code:

class A(object):
    def __init__(self, a, b):
        super(A, self).__init__()
        print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))

class B(object):
    def __init__(self, q):
        super(B, self).__init__()
        print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))

class C(A, B):
    def __init__(self):
        super(A, self).__init__(1, 2)
        super(B, self).__init__(3)

c = C()

renvoie l'erreur:

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    c = C()
  File "test.py", line 13, in __init__
    super(A, self).__init__(1, 2)
TypeError: __init__() takes 2 positional arguments but 3 were given

J'ai trouvé cette ressource qui explique l'héritage mutiple avec différents ensembles d'arguments, mais ils suggèrent d'utiliser *args et **kwargs à utiliser pour tous les arguments. Je considère cela comme très laid, car je ne peux pas voir de l'appel du constructeur dans la classe enfant quel type de paramètres je passe aux classes parent.

41
Nikolai Tschacher

Faites pas utilisez super(baseclass, ...) sauf si vous savez ce que vous faites. Le premier argument de super() lui indique quelle classe ignorer lors de la recherche de la prochaine méthode à utiliser. Par exemple. super(A, ...) va regarder le MRO, trouver A, puis commencer à chercher __init__ sur la classe de base suivante, pas = A lui-même. Pour C, le MRO est (C, A, B, object), Donc super(A, self).__init__ trouvera B.__init__.

Dans ces cas, vous ne souhaitez pas utiliser l'héritage coopératif mais référencer directement A.__init__ Et B.__init__ À la place. super() ne doit être utilisé que si les méthodes que vous appelez ont la même signature ou avalent des arguments non pris en charge avec *args et **vargs. Dans ce cas, un seul appel super(C, self).__init__() serait nécessaire et la classe suivante dans l'ordre MRO se chargerait de chaîner l'appel.

Autrement dit: lorsque vous utilisez super(), vous ne pouvez pas savoir quelle classe sera la prochaine dans le MRO, afin que cette classe prenne mieux en charge les arguments que vous lui passez. Si ce n'est pas le cas, n'utilisez pas super().

Appel direct des méthodes de base __init__:

class A(object):
    def __init__(self, a, b):
        print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))

class B(object):
    def __init__(self, q):
        print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))

class C(A, B):
    def __init__(self):
        # Unbound functions, so pass in self explicitly
        A.__init__(self, 1, 2)
        B.__init__(self, 3)

Utilisation de la coopérative super():

class A(object):
    def __init__(self, a=None, b=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print('Init {} with arguments {}'.format(self.__class__.__name__, (a, b)))

class B(object):
    def __init__(self, q=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print('Init {} with arguments {}'.format(self.__class__.__name__, (q)))

class C(A, B):
    def __init__(self):
        super().__init__(a=1, b=2, q=3)
77
Martijn Pieters