Published:
Tagged: Python
TLDR; Always call
super().__init__()
in the__init__()
method of the subclass.
I’ve started learning Python recently and came across a puzzling issue. I believe it’s a very likely trap for newcomers so here is a quick write up.
Say we have 2 standard classes - nothing special about them - they both define a name
property as well as a prop_a
and prop_b
property respectively:
class A:
def __init__(self):
self.name = "class A"
self.prop_a = "prop_a"
class B:
def __init__(self):
self.name = "class B"
self.prop_b = "prop_b"
And we’ll create another class C
that inherits from both and we’ll instantiate an object with it:
class C(A, B):
def __init__(self):
super().__init__()
self.prop_c = "prop_c"
c = C()
As you can expect, the name property is “taken” from the first parent class, A
so c.name
will return class A
. And it should also inherit both prop_a
and prop_b
, right?! Well, not quite. Let’s see what happens when we try to access them:
print(c.name) # prints "class A"
print(c.prop_a) # prints "prop_a"
print(c.prop_b) # returns an AttributeError (see below)
Traceback (most recent call last):
File "/Users/paul/.../multi-inheritance-gotcha.py", line 19, in
print(c.prop_b) # prop_b
^^^^^^^^
AttributeError: 'C' object has no attribute 'prop_b'. Did you mean: 'prop_a'?
HA! Same thing happens with prop_a
if we change the order of the multiple inheritance to class C(B, A)
. So what’s going on here? Why does it work for prop_a
but not for prop_b
? Looks like the second parent class is not being inherited at all.
We can fix it by calling the __init__()
method of the second parent class in the C
class:
class C(A, B):
def __init__(self):
super().__init__()
B.__init__(self)
self.prop_c = "prop_c"
In fact we can do the same for both parent classes (no need for super().__init__()
):
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
self.prop_c = "prop_c"
Turns out that the behavior is caused by not calling super().__init__()
in the __init__()
method of the parent classes. So if we add it to both A
and B
classes, the problem is solved:
class A:
def __init__(self):
super().__init__()
self.name = "class A"
self.prop_a = "prop_a"
class B:
def __init__(self):
super().__init__()
self.name = "class B"
self.prop_b = "prop_b"
I still don’t understand how it works internally, but it must be something to do with the way Python handles multiple inheritance internally.
All classes in Python ultimately inherit from a base class called object
. So when we create a class like A
, it’s actually inheriting from object
.
But nobody tells you that you should call super().__init__()
in the __init__()
method of all of your classes. Lesson learnt! :D
Happy coding!