Как работает порядок разрешения методов (MRO) в Python 2

Ответ

MRO (Method Resolution Order) определяет последовательность, в которой Python ищет метод в иерархии классов при множественном наследовании. В Python 2 существовало два разных подхода к MRO, зависящих от типа класса.

Важно: Поведение "классических классов" является устаревшим. В современном Python 3 все классы являются "новыми" и используют исключительно алгоритм C3.

1. Классические классы (Old-style classes)

Это классы, которые не наследуются от object. Они использовали простой алгоритм "сначала в глубину, затем слева направо" (depth-first, left-to-right). Этот подход мог приводить к проблемам при ромбовидном наследовании.

# Этот код корректно работает только в Python 2
class A:
    def who_am_i(self): print("I am A")

class B(A):
    pass

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

# Порядок наследования: B, C
class D(B, C):
    pass

d = D()
d.who_am_i() # Вывод: I am A

Порядок поиска: D -> B -> A. Поиск доходит до A и останавливается, не проверяя класс C, хотя он ближе в иерархии.

2. Новые классы (New-style classes)

Это классы, которые явно или неявно наследуются от object. Для них используется более совершенный алгоритм C3-линеаризации. Он гарантирует, что дочерние классы всегда проверяются раньше родительских, решая проблему ромбовидного наследования.

# Этот код работает и в Python 2, и в Python 3
class A(object):
    def who_am_i(self): print("I am A")

class B(A):
    pass

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

# Порядок наследования: B, C
class D(B, C):
    pass

d = D()
d.who_am_i() # Вывод: I am C

Порядок поиска (MRO): D -> B -> C -> A -> object. Поиск находит метод в C после проверки B и вызывает его.

Посмотреть MRO для класса можно с помощью атрибута __mro__ или метода .mro():

print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)