Что такое MRO?
MRO (Method Resolution Order) — линейный порядок классов, по которому Python ищет атрибут/метод. Доступен через Cls.__mro__ / Cls.mro(). Строится C3-линеаризацией: сохраняет local precedence order и monotonicity, при конфликте — TypeError при объявлении класса.
Что это
MRO — Method Resolution Order, линейный список классов, по которому Python ищет атрибут при obj.attr. Когда обращение не находит attribute в obj.__dict__, поиск идёт по type(obj).__mro__ слева направо. super() также делегирует следующему классу в MRO текущего экземпляра.
Доступ: Cls.__mro__ (tuple) или Cls.mro() (list). Всегда заканчивается на object.
Алгоритм C3
MRO вычисляется при создании класса по алгоритму C3 (Dylan, 1996; для Python — 2.3, new-style classes). Гарантии:
- Local precedence order: порядок прямых родителей сохраняется. В
class D(B, C)B идёт раньше C. - Monotonicity: если X идёт до Y в MRO предка, то и в MRO любого потомка.
- Consistency: MRO потомка согласован с MRO каждого предка.
Если согласованного порядка нет — Python бросает TypeError: Cannot create a consistent method resolution order.
Пример ромба
class A:
def ping(self):
return "A"
class B(A):
pass
class C(A):
def ping(self):
return "C"
class D(B, C):
pass
print([cls.__name__ for cls in D.__mro__])
# ['D', 'B', 'C', 'A', 'object']
print(D().ping()) # 'C' — потому что C идёт раньше A в MRO
# Конфликт MRO — TypeError при объявлении
try:
class X(B, A, C):
pass
except TypeError as e:
print(e)
# Cannot create a consistent method resolution order (MRO) for bases A, C
super() и MRO
class A:
def m(self):
return "A"
class B(A):
def m(self):
return "B->" + super().m()
class C(A):
def m(self):
return "C->" + super().m()
class D(B, C):
def m(self):
return "D->" + super().m()
print(D.__mro__)
# (D, B, C, A, object)
print(D().m()) # D->B->C->A
# super() идёт по MRO, A вызывается ровно один раз (diamond решён)
Когда это важно
- Кооперативные mixin-ы: каждый mixin вызывает
super().__init__(**kwargs)и пробрасывает остальные kwargs дальше по MRO. - Django/DRF view, SQLAlchemy declarative mixins, pytest plugins — везде, где собирают функциональность.
- Отладка «откуда взялся этот метод» — печатайте MRO и смотрите первый класс, где метод определён.
Подводные камни
- Считать MRO просто DFS слева направо — это было в Python 2.2 (old-style); новый style использует C3, на ромбах порядок другой.
- Конфликт MRO при объявлении:
class D(B, A)когдаB(A); решайте через корректный порядок. - Mixin не зовёт
super().__init__()— цепочка обрывается, следующие в MRO не инициализируются. - Передача
**kwargsвobject.__init__— он не принимает аргументов; последний mixin перед object должен «съесть» остатки. - Использовать
super(ClsName, self)явно — устаревший стиль; в Py3 пишите простоsuper(). - Изменять
__bases__на лету или динамически делать классы — MRO пересчитывается, читать такой код больно.
Common mistakes
- Не знать mro.
- Не объяснять ромбовидное наследование.
- Путать MRO и порядок создания объектов.
What the interviewer is testing
- Может прочитать mro.
- Понимает связь с super.
- Знает, что алгоритм — C3.