Что такое MRO
MRO (Method Resolution Order) — это порядок разрешения методов в языках с множественным наследованием, определяющий, в каком порядке интерпретатор ищет метод или атрибут в иерархии классов. В Python MRO реализован через C3-линеаризацию и доступен через атрибут __mro__ класса.
Как это работает
В Python при обращении к атрибуту или методу объекта интерпретатор следует MRO, чтобы найти первое подходящее определение. MRO строится на основе обхода графа наследования в глубину слева направо (DFLR) с последующим удалением дубликатов, кроме последнего вхождения. Этот процесс называется C3-линеаризацией и гарантирует, что каждый класс в иерархии появляется в MRO ровно один раз, а порядок сохраняет согласованность с порядком базовых классов.
Для классических классов (Python 2) использовался простой DFLR-обход, но в новых стилях (Python 3) применяется C3-линеаризация, которая решает проблему ромбовидного наследования.
Пример
Рассмотрим два примера: неромбовидное и ромбовидное наследование.
Неромбовидное наследование
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.
Ромбовидное наследование
class D:
attr = 3
class B(D):
pass
class C(D):
attr = 1
class A(B, C):
pass
x = A()
print(x.attr) # 1DFLR-обход дал бы [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, поэтому порядок вызовов зависит от иерархии классов.
