pythonpython-3.xmultiple-inheritancemethod-resolution-order

understanding MRO, constructors, and methods in multiple inheritance of python


following is the two set of codes: one having constructor __init__(), and another having display() method.

code one:

class A(object):
    def __init__(self):
        self.a = "a"
        print(self.a)
        super().__init__()

class B(object):
    def __init__(self):
        self.b = "b"
        print(self.b)
        super().__init__()

class C(A,B):
    def __init__(self):
        self.c = "c"
        print(self.c)
        super().__init__()

o = C()
print(C.mro())

if we compile and run this above set of codes, output will be:

c
a
b
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

now, Second block of code is below,

class A(object):
    def display(self):
        self.a = "a"
        print(self.a)
        super().display()

class B(object):
    def display(self):
        self.b = "b"
        print(self.b)
        super().display()

class C(A,B):
    def display(self):
        self.c = "c"
        print(self.c)
        super().display()

o = C()
o.display()
print(C.mro())

if in above second block of code we replace __init__() with display() method then, BECAUSE OF super().display() in class B, error will be shown also if we remove super().display() in class B their will be no error

AttributeError: 'super' object has no attribute 'display'

which I understand, because OBJECT class does not have display() method in it. but in the first block of code, the code runs without error.

Does that means OBJECT CLASS of python have __init__() in it?

if not, please explain me why their is no error in first block of code but their is error in second block of code?


Solution

  • Let's break this code down:


    First snippet:

    class A(object):
        def __init__(self):
            self.a = "a"
            print(self.a)
            super().__init__()
    
    
    class B(object):
        def __init__(self):
            self.b = "b"
            print(self.b)
            super().__init__()
    

    This is a regular class in Python, no need to elaborate - it inherits from object - (which in Python3 is redundant) it calls a the super().__init__() which translates to object().__init__() - every class in Python inherits from object - we can see it by calling the method resolution order and noticing <class 'object'> in the list (last element/ only element). Same story for class B.

    class C(A,B):
        def __init__(self):
            self.c = "c"
            print(self.c)
            super().__init__()
    

    Here we have complicated things up - we have inherited from two classes: A and B. Also calling super().__init__() - which calls A.__init__() and B.__init__() - thus printing in the order you've seen: c, a, b.


    Second snippet:

    This where things break.

    class A(object):
        def display(self):
            self.a = "a"
            print(self.a)
            super().display()
    

    This is again, a regular class in Python with the redundant inheritance from the object class - but now we are calling super().display() - which translates to object().display() - and that method does not exist:

    
    >>> object().__init__()
    
    None
    
    
    >> object.display()
    
    AttributeError: type object 'object' has no attribute 'display'
    

    TL;DR:

    Yes, the object class does have __init__(), here it is:

    def __init__(self): # known special case of object.__init__
        """ Initialize self.  See help(type(self)) for accurate signature. """
        pass
    

    no, it does not have .display().