Search code examples
pythonmultiple-inheritancemethod-resolution-order

What happens if define a class without __init__ method?


Suppose that I have defined four classes as following:

(The code has been tested on Python 3.6.5. However, I expected it should also works on Python 2.7.x with from __future__ import print_function)

In [1]: class A(object):
   ...:     pass
   ...: 
   ...: class B(object):
   ...:     def __init__(self, value):
   ...:         print('B(value=%s)' % value)
   ...: 
   ...: class C(A):
   ...:     def __init__(self, value):
   ...:         print('C(value=%s)' % value)
   ...:         super(C, self).__init__(value)
   ...: 
   ...: class D(A, B):
   ...:     def __init__(self, value):
   ...:         print('D(value=%s)' % value)
   ...:         super(D, self).__init__(value)
   ...:         

In [2]: C.mro()
Out[2]: [__main__.C, __main__.A, object]

In [3]: D.mro()
Out[3]: [__main__.D, __main__.A, __main__.B, object]

Note two things:

  1. class A without __init__ method;

  2. Both C and D have the same successor A according the mro information.

So I suppose that both super(C, self).__init__(value) and super(D, self).__init__(value) will trigger the __init__ method defined in A.

However, the following result confused me very much!

In [4]: C(0)
C(value=0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-75d9b7f7d447> in <module>()
----> 1 C(0)

<ipython-input-1-5252938615c6> in __init__(self, value)
      9     def __init__(self, value):
     10         print('C(value=%s)' % value)
---> 11         super(C, self).__init__(value)
     12 
     13 class D(A, B):

TypeError: object.__init__() takes no parameters

In [5]: D(1)
D(value=1)
B(value=1)
Out[5]: <__main__.D at 0x2a6e8755b70

We can see that class D have initialization succeed, while class C failed.

What make the different behavior between class C and class D?

EDIT I don't think my question is about the mro (actually I known the mro about python more or less), my question is about the different behavior of __init___ method.

EDIT2 I'm silly, it's about mro.

Thanks for your help.


Solution

    1. Class A has no separate __init__ method - it is looked up via A `s own mro.

      >>> A.__init__ is object.__init__
      True
      
    2. Class B has a separate __init__ method - it does not require traversing the mro.

      >>> B.__init__ is object.__init__
      False
      

    Note that the difference is having and inheriting __init__. Just because A.__init__ can be provided does not mean A has an __init__ method by itself.

    1. When C looks up its super().__init__, the following is tried:

      • C.__init__ is skipped
      • A.__init__ does not exist
      • object.__init__ exists and is called
    2. When D looks up its super().__init__, the following is tried:

      • D.__init__ is skipped
      • A.__init__ does not exist
      • B.__init__ exists and is called
      • object.__init__ is never looked up

    This difference is a major point to using super instead of an explicit base class. It allows inserting specialised classes into a hierarchy (example).