Search code examples
pythonpython-2.7classinheritancemultiple-inheritance

Multiple inheritance and passing arguments to inherited class in python


I have the following classes defined:

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

    def mA1(self):
        print "Method 1 in A"

    def mA2(self):
        print "Method 2 in A"

    def mA3(self):
        print "Method 3 in A"

class B(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.x = 0

    def mB1(self):
        print "Method 1 in B"

class C(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = ""
        self.y = 1

    def mC1(self):
        print "Method 1 in C"

    def mC2(self):
        print "Method 2 in C"

As it can be seen, they take the same input arguments in the constructor. Now, I want to create a class D which inherits from all of A, B and C so that I can directly pass the arguments to D's constructor as follows:

clsT = D(1, 2)

So, I tried out as mentioned here the following revised definitions:

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

    def mA1(self):
        print "Method 1 in A"

    def mA2(self):
        print "Method 2 in A"

    def mA3(self):
        print "Method 3 in A"


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

    def mB1(self):
        print "Method 1 in B"


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

    def mC1(self):
        print "Method 1 in C"

    def mC2(self):
        print "Method 2 in C"


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


ci = D(1, 2)

print "a = ", ci.a
print "b = ", ci.b
print "c = ", ci.c
print "x = ", ci.x
print "y = ", ci.y

The above doesn't seem to work and gives me the following error:

    class D(A, B, C):
TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases B, C, A

What could possibly be going wrong? Is it really required that B and C must include super(), creating a linear inheritance at the mid-level base classes? Is there an alternative to my interpretation? If not, how can mine be tweaked to make it work?


Solution

  • I changed the class defintion of D as follows and it worked

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

    So the overall class will look like, (I removed the methods to keep the code short)

    class A(object):
        def __init__(self, a, b):
            self.a = a
            self.b = b
            self.c = ""
    
    class B(A):
        def __init__(self, **kw):
            super(B, self).__init__(**kw)
            self.x = 0
    
    class C(A):
        def __init__(self, **kw):
            super(C, self).__init__(**kw)
            self.c = ""
            self.y = 1
    
    
    class D(B, C, A):
        def __init__(self, a, b):
            super(D, self).__init__(a=a, b=b)
    
    
    c = D(1, 2)
    print(c.a, c.b, c.x, c.c, c.y)
    

    The output will be

    1 2 0  1
    

    This is due to a rule in the MRO algorithm which says (More details in this answer here, but the gist is

    a class always appears before its ancestor ("monotonicity")

    Hence B and C needs to appear before A, since A is the ancestor of B and C

    Or in other words: D is inheriting from A,B and C. Because B and C already inherits from A, there is no way python now cannot determine what class to look methods up on first; either A, or B and C, if you use the old order of defining. D(A, B, C)