MRO в Python: почему D().f() возвращает 'C'
@python_quizРазберем этот квиз
Задача короткая: в наборе классов с множественным наследованием вызывается метод f у экземпляра класса D. Нужно понять, какой метод будет использован и почему.
Исходный код:

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

Ожидаемый вывод:

Анализ вариантов ответа (логика, почему одни варианты неверны)
- "'A'": такой результат был бы возможен, если бы ни в одной из промежуточных базовых классов не было переопределения f, а следующей по порядку стояла A. Но C переопределяет f и стоит раньше A в MRO, поэтому 'A' не используется.
- "'B'": B не определяет метод f, только наследует его от A. Наличие B в MRO не даёт метода f, поэтому поиск продолжается дальше.
- "'C'": метод f найден в C — именно этот вариант соответствует логике MRO и фактическому выполнению кода.
- "Ошибка MRO при создании D": C3-линеаризация корректно обрабатывает такой простой случай (D(B, C) с B и C обоими наследующими A) — конфликтов нет, создаётся нормальный MRO, ошибки не возникает.
Дополнительные примеры для закрепления
- Если поменять порядок наследования в D на D(C, B), MRO станет D, C, B, A и результат останется 'C' (потому что C всё ещё перед B).
- Если бы B переопределял f, например:

то при D(B, C) результат был бы 'B' (B стоит раньше C в MRO).
Выводы и рекомендации
- При отладке поведения методов в иерархиях множественного наследования сначала смотрите на MRO (D.
__mro__или help(Класс)). - C3-линеаризация гарантирует детерминированный и понятный порядок поиска методов — важно учитывать порядок базовых классов при объявлении класса-потомка.
- Практика: если классы в иерархии переопределяют одинаковые методы, явно документируйте ожидаемый порядок и, при необходимости, используйте композицию или явные вызовы конкретных методов базовых классов (super() с учётом MRO), чтобы избежать неожиданных эффектов.