In fact, the super
Python does more than locate the explicit ancestors of a class. If he did just that, he might as well not even exist - he was always just explicitly putting the parent class in the code - the super
even make it less explicit, and maybe it would be better not to use.
What the super
does actually is to find the correct inheritance line in multiple inheritance hierarchies - therein lies the magic of it!
Note that this class is a "mixin" - in Python, in general classes of this type are intended to be combined with other "mixin" in a common hierarchy - and the inheritance diagram not only tends to get complicated, but it can’t even be predicted by those who write the mixin - (even more in this case that are coigos written at different times - the mixin is in the framework, and the code that will inherit from it will be written by the user of the framework, along with the proper classes of the system there).
Now, if all the relevant methods call your ancestor with the super
, no matter the order of composition of the final class: all methods will be executed.
And yes, all classes inherit from object
, then even if this is the last class placed in the hierarchy of heritages, the super
called from it will still call the corresponding method in object
Python has a very cool algorithm to determine the call order of methods, normally referred to only by the English acronym "mro" (method Resolution order). Formally it is complicated, but intuitively, it does "the right thing". This article that I linkey is the official documentation of the algorithm.
In the program, this order is explicit in any class in the attribute __mro__
- this is the order of ancestry considered when the super()
makes your call.. If you find code with any class that makes use of this mixin, and print <nome_da_classe>.__mro__
will see her there in the middle.
Here is an example of a class hierarchy with multiple inheritance- see how all methods __init__
are called:
class Base:
def __init__(self):
print(__class__.__name__)
super().__init__()
class MixinBranch1(Base):
def __init__(self):
print(__class__.__name__)
super().__init__()
class MixinBranch2(Base):
def __init__(self):
print(__class__.__name__)
super().__init__()
class Branch1(MixinBranch1):
def __init__(self):
print(__class__.__name__)
super().__init__()
class Final(MixinBranch2, Branch1):
def __init__(self):
print(__class__.__name__)
super().__init__()
And in interactive mode:
In [177]: Final.__mro__
Out[177]:
(__main__.Final,
__main__.MixinBranch2,
__main__.Branch1,
__main__.MixinBranch1,
__main__.Base,
object)
In [178]: Final()
Final
MixinBranch2
Branch1
MixinBranch1
Base
(this example uses another little known Python 3 Feature which is the magic variable __class__
(not to be confused with self.__class__
) - __class__
is an automatic reference to the class where is the code that makes use of it, no matter if the method was called a sub-class.)
is not the "reason to be".
– jsbueno