Встречается на 23% собеседований по Python

Что такое MRO

MRO (Method Resolution Order) — это порядок разрешения методов в языках с множественным наследованием, определяющий, в каком порядке интерпретатор ищет метод или атрибут в иерархии классов. В Python MRO реализован через C3-линеаризацию и доступен через атрибут __mro__ класса.

Как это работает

В Python при обращении к атрибуту или методу объекта интерпретатор следует MRO, чтобы найти первое подходящее определение. MRO строится на основе обхода графа наследования в глубину слева направо (DFLR) с последующим удалением дубликатов, кроме последнего вхождения. Этот процесс называется C3-линеаризацией и гарантирует, что каждый класс в иерархии появляется в MRO ровно один раз, а порядок сохраняет согласованность с порядком базовых классов.

Для классических классов (Python 2) использовался простой DFLR-обход, но в новых стилях (Python 3) применяется C3-линеаризация, которая решает проблему ромбовидного наследования.

Пример

Рассмотрим два примера: неромбовидное и ромбовидное наследование.

Неромбовидное наследование

python
class D:
    attr = 3

class B(D):
    pass

class E:
    attr = 2

class C(E):
    attr = 1

class A(B, C):
    pass

x = A()
print(x.attr)  # 3

Здесь MRO для A будет [A, B, D, C, E, object]. Поиск attr начинается с A, затем B, затем D, где находится attr = 3, поэтому выводится 3.

Ромбовидное наследование

python
class D:
    attr = 3

class B(D):
    pass

class C(D):
    attr = 1

class A(B, C):
    pass

x = A()
print(x.attr)  # 1

DFLR-обход дал бы [A, B, D, C, D], но C3-линеаризация удаляет первый дубликат D, оставляя [A, B, C, D, object]. Поэтому attr находится в C (значение 1), а не в D. Это ключевое отличие от классических классов, где выводилось бы 3.

Подводные камни

  • Ромбовидное наследование: без C3-линеаризации порядок может быть неоднозначным. В Python 3 проблема решена, но важно помнить, что порядок базовых классов в определении класса влияет на MRO.
  • super(): функция super() использует MRO для вызова следующего метода в цепочке. Неправильное понимание MRO может привести к неожиданному порядку вызовов.
  • Изменение MRO: напрямую изменять __mro__ нельзя, но можно влиять на него через порядок базовых классов и использование миксинов.

Когда использовать

Знание MRO необходимо при проектировании сложных иерархий классов, особенно с множественным наследованием. Оно помогает предсказать, какой метод будет вызван, и правильно использовать super(). Для простых иерархий MRO не критично, но понимание его принципов полезно для отладки.

Коротко

  • MRO — порядок разрешения методов, определяющий, где искать метод при множественном наследовании.
  • В Python 3 MRO строится по алгоритму C3-линеаризации, доступен через __mro__.
  • В ромбовидном наследовании C3-линеаризация устраняет дубликаты, сохраняя последний, что меняет результат по сравнению с классическими классами.
  • super() следует MRO, поэтому порядок вызовов зависит от иерархии классов.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы