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:
class A
without __init__
method;
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.
Class A
has no separate __init__
method - it is looked up via A
`s own mro
.
>>> A.__init__ is object.__init__
True
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.
When C looks up its super().__init__
, the following is tried:
C.__init__
is skippedA.__init__
does not existobject.__init__
exists and is calledWhen D looks up its super().__init__
, the following is tried:
D.__init__
is skippedA.__init__
does not existB.__init__
exists and is calledobject.__init__
is never looked upThis difference is a major point to using super
instead of an explicit base class. It allows inserting specialised classes into a hierarchy (example).