Reputation: 8072
According to Python docs super()
is useful for accessing inherited methods that have been overridden in a class.
I understand that super refers to the parent class and it lets you access parent methods. My question is why do people always use super inside the init method of the child class? I have seen it everywhere. For example:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def __init__(self, **kwargs):
super().__init__(name=kwargs['name']) # Here super is being used
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
I can accomplish the same without super and without even an init method:
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def first_letter(self):
return self.name[0]
e = Employee(name="John")
print(e.first_letter())
Are there drawbacks with the latter code? It looks so much cleanr to me. I don't even have to use the boilerplate **kwargs and kwargs['argument'] syntax.
I am using Python 3.8. Edit: Here's another stackoverflow questions which has code from different people who are using super in the child's init method. I don't understand why. My best guess is there's something new in Python 3.8.
Upvotes: 2
Views: 1090
Reputation: 483
The potential drawback to the latter code is that there is no __init__
method within the Employee class. Since there is none, the __init__
method of the parent class is called. However, as soon as an __init__
method is added to the Employee class (maybe there's some Employee-specific attribute that needs to be initialized, like an id_number) then the __init__
method of the parent class is overridden and not called (unless super.__init__()
is called) and then an Employee will not have a name
attribute.
Upvotes: 1
Reputation: 532323
The correct way to use super
here is for both methods to use super
. You cannot assume that Person
is the last (or at least, next-to-last, before object
) class in the MRO.
class Person:
def __init__(self, name, **kwargs):
super().__init__(**kwargs)
self.name = name
class Employee(Person):
# Optional, since Employee.__init__ does nothing
# except pass the exact same arguments "upstream"
def __init__(self, **kwargs):
super().__init__(**kwargs)
def first_letter(self):
return self.name[0]
Consider a class definition like
class Bar:
...
class Foo(Person, Bar):
...
The MRO for Foo
looks like [Foo, Person, Bar, object]
; the call to super().__init__
inside Person.__init__
would call Bar.__init__
, not object.__init__
, and Person
has no way of knowing if values in **kwargs
are meant for Bar
, so it must pass them on.
Upvotes: 1
Reputation: 6826
The child might want to do something different or more likely additional to what the super class does - in this case the child must have an __init__
.
Calling super’s init means that you don’t have to copy/paste (with all the implications for maintenance) that init in the child’s class, which otherwise would be needed if you wanted some additional code in the child init.
But note there are complications about using super’s init if you use multiple inheritance (e.g. which super gets called) and this needs care. Personally I avoid multiple inheritance and keep inheritance to aminimum anyway - it’s easy to get tempted into creating multiple levels of inheritance/class hierarchy but my experience is that a ‘keep it simple’ approach is usually much better.
Upvotes: 3