Вы всегда можете проконсультироваться по __mro__
, чтобы узнать, что происходит:
print(c.__class__.__mro__)
# (<class '__main__.child'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class '__main__.GrandPa'>, <class 'object'>)
И действительно, Father
идет перед Mother
в этой цепочке.
Но когда вы объединяете такие вызовы в цепочку, вот что происходит на самом деле:
class GrandPa:
def __init__(self):
print("Grand Pa")
class Father(GrandPa):
def __init__(self):
print('f', super())
super().__init__()
print("Father")
class Mother(GrandPa):
def __init__(self):
print('m', super())
super().__init__()
print("Mother")
class child(Father, Mother):
def __init__(self):
print('c', super())
super().__init__()
print("Child")
c = child()
Я добавил print(super())
ко всем методам, чтобы проиллюстрировать, что происходит внутри стека вызовов:
c <super: <class 'child'>, <child object>>
f <super: <class 'Father'>, <child object>>
m <super: <class 'Mother'>, <child object>>
Grand Pa
Mother
Father
Child
Как видите, мы начинаем с Child
, затем с go до Father
, затем до Mother
, и только после этого выполняется GrandPa
. Затем поток управления возвращается к Mother
и только после того, как он снова переходит к Father
.
В реальных случаях использования множественного наследования не рекомендуется злоупотреблять. Это может быть огромная боль. Состав и миксины легче понять.