January 12

MRO или Method Resolution Order в Python

Что такое MRO

MRO - Это порядок разрешения методов и о нем обычно говорят, когда речь заходит о множественном наследовании (кстати, если с ним не знакомы, настоятельно рекомендую немного покопаться в теме).

Он определяет последовательность, по которой Python ищет методы и атрибуты в классе и его родителях. Чтобы чуть глубже понять, предлагаю рассмотреть принцип его работы на конкретном примере.

Разбираемся с MRO на примере

Допустим, у нас есть некий класс D, который наследуется от классов B и C, которые наследуются от класса A, который ... :) пожалуй, остановимся на этом.

У этих классов определены методы who_am_i, которые просто печатают имя класса, которому они принадлежат. Классу D мы не будем создавать этот метод, а сделаем из него просто класс-заглушку:

class A:
    def who_am_i(self):
        print("A") 


class B(A): 
    def who_am_i(self):
        print("B") 


class C(A):
    def who_am_i(self):
        print("C") 


class D(B, C):
    ...

Теперь создадим экземпляр класса D и попробуем вызвать у него метод who_am_i:

d = D()
d.who_am_i()

Что произойдет? А вот что. В выводе мы увидим:

>>> B

Почему именно B, нам может рассказать MRO. Для удобства можно просто вызвать метод mro() или атрибут __mro__:

print(D.mro())  # или: print(D.__mro__)
>>> (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

Из вывода консоли можно увидеть порядок обращений. Сначала метод ищется в классе D, но его там нет, поэтому Python смотрит в следующем классе - B. Метод who_am_i есть у класса B, поэтому именно он и вызывается. Соответственно, при печати мы и видим B.

Почему MRO работает именно так?

Во всем виновата линеаризация и конкретно алгоритм C3 linearization. Разберем кратко, что это такое.

Линеаризация – это упорядочение классов (включая сам класс и его родителей) в список, отсортированный в порядке их "Удаленности". Этот список Python использует для поиска методов и атрибутов. А алгоритм C3 определяет три главные особенности такого порядка:

  • устойчивый и расширяющийся (по старшинству)
  • сохранение локального порядка старшинства
  • монотонность

Почитать подробнее и ознакомиться с примерами можно на Вики

Таким образом, MRO определяет, как Python ищет методы и атрибуты в классах при множественном наследовании, делая этот процесс предсказуемым и логичным.