7

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'
Bob Sacamano
  • 699
  • 15
  • 39
  • Your file name is also **c.py**. This can also popup another error. There for change your file name also. – Kalana May 03 '20 at 12:56
  • When using `super` calling of parent classes happens as per the `mro` order. Based on your inheritance level, the constructor calls follow order `I E D H G F C B A`. So `super` in `D` calls `__init__` of `H` instead of `C`. – Raj May 03 '20 at 13:53

3 Answers3

3

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.

SajanGohil
  • 960
  • 13
  • 26
3

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)
Welcome_back
  • 1,245
  • 12
  • 18
0

It's difficult to know what's the business case you're trying to deal with, and what drives this specific form of inheritance. That being said, you're getting the exception because the class 'I' first calls init of 'E', and only then of 'H'.

The easiest way to solve the issue would be to switch the order of I's base-classes to what seems more natural in your case:

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())

This solves the issue.

Roy2012
  • 11,755
  • 2
  • 22
  • 35