Is there a difference between using super() and using the parent class name directly? For example:
class Parent:
def __init__(self):
super().__init__(*args, **kwargs)
Perceive you don't pass the "self" - it is inserted automatically.
super() was first designed in Python 2 to allow classes to be reused as mixins in a class hierarchy in a way that their immediate superclass may change:
Let's supose at some point in time your code is like:
class A: pass
class B(A):
def __init__(self, *args, **kwargs):
...
# Fixed call to A
A.__init__(self, *args, **kwargs)
class C(A):
def __init__(self, *args, **kwargs):
...
# Fixed call to A
A.__init__(self, *args, **kwargs)
class D(C, B):
pass
At this point, correct OOP code should execute C.__init__ which should chain the call to B.__init__: but when the superclass name is hardcoded that does not happen - A's __init__ would always come next.
And if you hardcode B.__init__ in C, you would prevent C from working without B, defeating the purpose of multiple inheritance.
When you use super() instead, Python's perform the method search for the next parent class looking on the class's __mro__ attribute (mro = method resolution order. __mro__ is a concrete attribute attached to each Python class). - So, if at some point in time class D above no longer inherits from B, the calls to super().__init__ in C will be automatically re-routed straight to A.
It is also worth noting that in Python 3 the parameterless form of super was introduced to ease its use - prior to that, one had to hardcode a reference to the own class and also insert self in the parameters. This form is one of the few exceptions in Python that is hardcoded in the compiler itself - it do change things internally on methods when super (or __class__) is seen inside a method body (namely, it creates a __class__ variable pointing to the class itself which the super call uses)