I know of two ways to call a superclass's constructor from a derived class: super().__init__()
and base.__init__(self)
. Why does the second require me to explicitly supply self
as an argument?
Here's a snippet that demonstrates the difference:
class base:
def __init__(self,/):
print("initializing")
class der(base):
def __init__(self):
super().__init__() #no error
base.__init__(self) #no error
base.__init__() #error, self required
obj = der()
The third constructor call throws this error:
File "demo.py", line 11, in <module>
obj = der()
^^^^^
File "demo.py", line 9, in __init__
base.__init__() #error, should be: base.__init__(self)
^^^^^^^^^^^^^^^
TypeError: base.__init__() missing 1 required positional argument: 'self'
I expect base.__init__()
to work since super().__init__()
doesn't require an explicit self
.
TL;DR: super()
is an instance. der
is a class.
To undersand this properly, you have to understand how instance methods in Python work. When you write an instance method, you have to include self
as the first parameter like this:
class Foo:
def bar(self): ...
This is because when you call an instance method, Python automatically supplies the instance itself as the method's first argument under the hood. These two lines will do the same thing:
x.bar()
Foo.bar(x)
The only difference is that the first line calls bar
from an instance and the second calls bar
from a class.
In your example, base
is a class (the equivalent to Foo
) instead of an instance (the equivalent of x
). Thus, if you want to call the base
constructor, you need to manually supply self
- Python won't do it for you.
The super
version doesn't need that because super
isn't the class base
- it's a proxy for the instance self
that pretends it's of type der
instead of type base
. That means super()
is still an instance, not a class, so super().__init__()
will automatically supply self
as a parameter to base.__init__
and you don't need to do it manually.