Ошибка типа: python множественное наследование с разными аргументами - PullRequest
8 голосов
/ 30 апреля 2020

Я пытаюсь использовать множественное наследование, чтобы добавить некоторые функции в один из существующих классов, которые у меня есть. Проблема в том, что этот новый класс и мой текущий базовый класс имеют разные аргументы в своих конструкторах. А именно новый класс имеет 1 дополнительный аргумент. После некоторого поиска в Google я понял, что могу добавить ** kwargs к текущему базовому классу (тот, с одним аргументом меньше). Пример:

class A(object):
    def __init__(self, a):
        print('__init__', locals())


class B(A):
    def __init__(self, a, b):
        super(B, self).__init__(a)
        print('__init__', locals())


class C(B):
    def __init__(self, a, b):
        super(C, self).__init__(a, b)
        print('__init__', locals())


class D(C):
    def __init__(self, a, b):
        super(D, self).__init__(a, b)
        print('__init__', locals())


class E(D):
    def __init__(self, a, b, *args, **kwargs):
        super(E, self).__init__(a, b)
        print('__init__', locals())


class F(C):
    def __init__(self, a, b):
        super(F, self).__init__(a, b)
        print('__init__', locals())


class G(F):
    def __init__(self, a, b, c):
        super(G, self).__init__(a, b)
        print('__init__', locals())


class H(G):
    def __init__(self, a, b, c):
        super(H, self).__init__(a, b, c)
        print('__init__', locals())


class I(E, H):
    def __init__(self, a, b, c):
        super(I, self).__init__(a, b, c=c)
        print('__init__', locals())


for c in I.__mro__:
        print(c)


I(0, 1, 2)

Но я получаю эту ошибку:

<class '__main__.I'>
<class '__main__.E'>
<class '__main__.D'>
<class '__main__.H'>
<class '__main__.G'>
<class '__main__.F'>
<class '__main__.C'>
<class '__main__.B'>
<class '__main__.A'>
<class 'object'>
Traceback (most recent call last):
  File "/tmp/c.py", line 58, in <module>
    I(0,1,2)
  File "/tmp/c.py", line 50, in __init__
    super(I, self).__init__(a, b, c=c)
  File "/tmp/c.py", line 26, in __init__
    super(E, self).__init__(a, b)
  File "/tmp/c.py", line 20, in __init__
    super(D, self).__init__(a, b)
TypeError: __init__() missing 1 required positional argument: 'c'

Ответы [ 3 ]

3 голосов
/ 03 мая 2020

У меня этот код работает !!!

class A(object):
    def __init__(self, a):
        print("A class")


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



class C(B):
    def __init__(self, a, b):
        B.__init__(self,a, b)
        print("C class")



class D(C):
    def __init__(self, a,b):
        C.__init__(self,a, b)
        print("D class")


class F(C):
    def __init__(self, a,b):
        #C.__init__(self,a, b)
        print("F class")


class G(F):
    def __init__(self, a, b, c):
        F.__init__(self,a, b)
        print("G class")

class E(D):
    def __init__(self, a, b):
        D.__init__(self,a, b)
        print("E class")


class H(G):
    def __init__(self, a,b,c):
        G.__init__(self,a, b, c)
        print("H class")


class I(E,H):
    def __init__(self, a, b, c):
        args=(a,b,c)
        E.__init__(self,a,b)
        H.__init__(self,a,b,c)
        print('__init__', locals())


print(I.__mro__)
I(1,2,3)
3 голосов
/ 03 мая 2020

Согласно MRO, вызов переходит на H после D, и поэтому, если вам нужно отправить c, класс D должен будет принять его и отправить 3 параметра ie. H будет вызываться D. Например:

class A(object):
    def __init__(self, a):
        print('a')
        print('__init__', locals())

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

        print('__init__', locals())

class C(B):
    def __init__(self, a, b):
        print('c')
        super(C, self).__init__(a, b)

        print('__init__', locals())

class D(C):
    def __init__(self, a, b, *args, **kwargs):
        print('d', args, kwargs)
        super(D, self).__init__(a, b, args, kwargs)

        print('__init__', locals())

class E(D):
    def __init__(self, a, b, *args, **kwargs):
        print('e', args, kwargs)
        super(E, self).__init__(a, b)

        print('__init__', locals())

class F(C):
    def __init__(self, a, b):
        print('f')
        super(F, self).__init__(a, b)

        print('__init__', locals())

class G(F):
    def __init__(self, a, b, c):
        print('g')
        super(G, self).__init__(a, b)

        print('__init__', locals())

class H(G):
    def __init__(self, a, b, c, *args, **kwargs):
        print('h')        
        super(H, self).__init__(a, b, c)

        print('__init__', locals())

class I(E,H):
    def __init__(self, a, b, c):
        print('i')
        super(I,self).__init__(a, b, c)
        #E.__init__(self,a, b)
        #H.__init__(self,a, b, c)

        print('__init__', locals())

for c in I.__mro__:
        print(c)

I(0, 1, 2)

Этот код работает (я изменил c как аргумент вместо **kwarg). Другой способ - если вы поменяете порядок наследования E, H, MRO сработает, и вам не нужно будет это делать, или используйте E.__init__() и H.__init__() по отдельности. В этом случае MRO снова меняется, и общие классы будут вызываться дважды, если это необходимо.

Для MRO я нашел этот ответ и этот пост в блоге Гвидо Ван Россум (также связанный в другом ответе), который может помочь вам понять алгоритм MRO в python.

0 голосов
/ 10 мая 2020

Трудно понять, с каким бизнес-кейсом вы пытаетесь разобраться, и чем обусловлена ​​эта специфическая c форма наследования. При этом вы получаете исключение, потому что класс «I» сначала вызывает init для «E», а только потом для «H».

Самый простой способ решить эту проблему - переключить порядок базовых классов I на то, что кажется более естественным в вашем случае:

class I(H, E):  # original code was I(E, H) 
    def __init__(self, a, b, c):
        super(I, self).__init__(a, b, c=c)
        print('__init__', locals())

Это решает проблему.

...