MRO в Python: почему D().f() печатает 'B'
@python_quizРазберем этот квиз
Коротко: анализируем, как работает динамическое создание классов через type(), как Python разрешает множественное наследование и почему метод f для экземпляра D берётся из класса B.
Условие (код)
Вот исходный код, который разбираем:

Коротко: создаются четыре класса динамически. D наследует от B и C; B и C — от A. Метод f определён в A, B и C.
Что делает type(...) здесь
- type(name, bases, dict) — динамически создаёт класс с именем name, базовыми классами bases и атрибутами из dict.
- lambda s: "A" — это метод, ожидающий аргумент (обычно
self), возвращает строку с буквой класса.
Эквивалентный код в декларативном синтаксисе:

Поведение идентично.
Разрешение методов: MRO (Method Resolution Order)
При вызове метода для экземпляра Python ищет его в порядке, определённом MRO класса. В современных версиях Python используется алгоритм C3-линеаризации, который выстраивает единственный корректный линейный порядок обхода базовых классов при множественном наследовании.
Для нашего случая порядок будет таким:
D → B → C → A → object
Можно убедиться программно:

Вывод будет примерно:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Это означает: при поиске атрибута/метода Python будет смотреть сначала в D, затем в B, затем в C, затем в A, затем в object.
Почему печатается 'B'
- В D самого метода f нет.
- Первый класс в MRO после D — B. В B определён f, поэтому Python берёт этот метод и вызывает его.
- Метод B.f возвращает строку "B", поэтому печатается B.
Иными словами: метод найден в B по порядку MRO до того, как проверится C или A.
Анализ вариантов (коротко)
- "Выведет 'A'": неверно — A находится ниже в MRO, к нему дойдёт только если B и C не содержат f.
- "Выведет 'B'": соответствует MRO — верно, потому что B идёт раньше C и A.
- "Выведет 'C'": неверно — C идёт после B в MRO, поэтому его f не используется.
- "Ошибка MRO при создании класса D": неверно — MRO существует (C3 применим) и конфликтов нет, так как наследование B и C от общего A согласовано.
Демонстрация и проверка
Полный пример с выводом MRO и результата:

Ожидаемый вывод:
MRO: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
D().f() -> B
Вывод
При множественном наследовании важно помнить про C3-линеаризацию: порядок поиска метода определяется MRO, а не просто списком базовых классов слева направо. В данном примере метод f взят из B потому, что B идёт раньше C и A в MRO класса D.