Что такое MRO (method resolution order) в Python и как он работает при множественном наследовании?
Что проверяют
Понимаете ли вы, что super это не «вызов родителя», а переход к следующему классу в линеаризации конкретного объекта. Вопрос отделяет тех, кто пользуется миксинами осознанно, от тех, кто собирает иерархию наугад и правит её перестановкой базовых классов, пока не заработает.
Как отвечать
MRO это линейный порядок, в котором Python ищет атрибут: сам класс, потом его базы, в конце object. Порядок строится алгоритмом C3 в момент создания класса и доступен как атрибут mro.
Правила C3: класс всегда идёт раньше своих баз, порядок перечисления баз сохраняется, каждый класс встречается один раз, результат согласован с порядком у всех предков.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
D.__mro__ # D, B, C, A, objectГлавное следствие ромба: если метод в B вызывает super, у экземпляра D управление уйдёт в C, а не в A. Класс B ничего не знает про C, но цепочка построена по типу объекта. Поэтому кооперативное наследование требует, чтобы каждый участник звал super и пропускал дальше незнакомые ему аргументы.
Иногда согласованного порядка не существует: при противоречиво упорядоченных базах интерпретатор откажется создавать класс прямо на объявлении.
Где спотыкаются
Читают super как «родитель» и не могут объяснить, почему в ромбе общий предок инициализируется один раз, а метод соседней ветки всё-таки вызывается. Пока в голове дерево, а не список, поведение выглядит случайным.
Ставят миксин правее основного класса и удивляются, что его переопределения не действуют. Слева стоит то, что перекрывает: миксин идёт первым, базовый класс последним.
Обрывают цепочку. Один класс в середине не зовёт super либо принимает фиксированный набор аргументов и не передаёт остальное дальше. Часть инициализации молча не выполняется, а ошибка всплывает далеко от места и выглядит как баг соседнего модуля.