Поведение конструкторов при множественном наследовании

Порядок вывода: Base, Y, X, Z. Поиск super идёт по MRO Z → X → Y → Base, а печать происходит при возврате из вложенных вызовов.

Короткий ответ

Что ответить на собеседовании

Порядок вывода: Base, Y, X, Z. Поиск super идёт по MRO Z → X → Y → Base, а печать происходит при возврате из вложенных вызовов.

Подробный разбор

Ответ с пояснениями

Условие

Разъясните, как будет происходить инициализация при создании экземпляра класса Z, учитывая порядок вызовов super() и схему наследования Z(X, Y).

class Base:
    def __init__(self):
        print('Base')

class X(Base):
    def __init__(self):
        super().__init__()
        print('X')

class Y(Base):
    def __init__(self):
        super().__init__()
        print('Y')

class Z(X, Y):
    def __init__(self):
        super().__init__()
        print('Z')

Z()

Ответ

Порядок вывода: Base, Y, X, Z. Поиск super идёт по MRO Z → X → Y → Base, а печать происходит при возврате из вложенных вызовов.

При входе в Z.__init__ вызывается X.__init__. Его super() для экземпляра Z продолжает цепочку не напрямую в Base, а в Y. Затем Y вызывает Base.

Base
Y
X
Z

После печати Base управление возвращается в Y, затем в X и Z. Общий предок инициализируется один раз. Это работает благодаря согласованному использованию super() всеми участниками цепочки. Прямые вызовы Base.__init__(self) из обеих ветвей могли бы привести к повторной инициализации и нарушить кооперативное наследование.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее