In fact, super
of Python does more than find the explicit ancestors of a class. If it did just that, it might not even exist - it was just always putting the parent class explicitly in the code - super
would even make it less explicit, and maybe it was better not even use it.
What super
does for real is to find the correct inheritance line in multiple inheritance hierarchies - that's the magic of it!
Note that this class is a "mixin" - in Python, usually 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 as it does not nor how to be predicted by the one who writes 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 classes own system there).
Now, if all relevant methods call your ancestor with super
, it does not matter the composition order of the final class: all methods will be executed.
And yes, all classes inherit from object
, so even though this is the last class placed in the inheritance hierarchy, the super
called from it will still call the corresponding method in object
Python has a very nice algorithm to determine the order of methods call , usually referred to only by the acronym in English " mro " (method resolution order). Formally he is complicated but intuitively he does "the right thing". This artik that I linkey is the official documentation of the algorithm.
In the program, this order is explicit in any class in the __mro__
attribute - this is the order of ancestry considered when super()
makes its call .. If you find code with some 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 multi-class inheritance hierarchy- see how all __init__
methods 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 that is the magic variable __class__
(not to be confused with self.__class__
) - __class__
is an automatic reference to the class where the code that makes use of it , it does not matter if the method was called a subclass.)