Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: python multiple inheritance with different arguments

I'm trying to use multiple inheritance to add some functionality to one of the existing classes that I have. The problem is that this new class and my current base class have different arguments in their constructors. Namely the new class has 1 extra argument. After some googling I understood that I can add **kwargs to the current base class(the one with one argument less). Example:

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)

But I get this error:

<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'
like image 615
Bob Sacamano Avatar asked Apr 30 '20 17:04

Bob Sacamano


2 Answers

This code work for me!!!

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)
like image 144
Welcome_back Avatar answered Nov 13 '22 18:11

Welcome_back


As per the MRO, the call goes to H after D, and so, if you need to send c, class D will need to accept it and send 3 parameters, ie. H will be called by D. E.G.:

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)

This code works, (I have changed c as an arg instead of **kwarg). Other way would be if you swap the E, H inheritance order, the MRO works out and you won't need to do it, or use E.__init__() and H.__init__() separately. In that case, MRO changes again, and common classes will be called twice if needed.

For MRO, I have found this answer and this blog post by Guido Van Rossum (also linked in the other answer) which might help you get insight on the algorithm for MRO in python.

like image 35
SajanGohil Avatar answered Nov 13 '22 19:11

SajanGohil