← Назад к вопросам

Как работает MRO?

3.0 Senior🔥 81 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает MRO (Method Resolution Order)

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

Основной принцип

При вызове метода Python проходит список классов в определённом порядке, пока не найдёт нужный метод. Без MRO множественное наследование было бы непредсказуемо.

Алгоритм C3 Linearization

Python использует алгоритм C3 Linearization для вычисления MRO. Это гарантирует:

  • Локальный приоритет: порядок базовых классов соблюдается
  • Монотонность: если класс A появляется раньше B в списке для класса C, то A должен появляться раньше B для всех подклассов C
  • Избежание парадоксов Diamond Problem: каждый класс появляется ровно один раз

Примеры MRO

Простое наследование:

class Animal:
    pass

class Dog(Animal):
    pass

class Bulldog(Dog):
    pass

print(Bulldog.__mro__)
# (<class Bulldog>, <class Dog>, <class Animal>, <class object>)

# Или используй
print(Bulldog.mro())

Множественное наследование:

class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):
    pass

print(D.mro())
# [<class D>, <class B>, <class C>, <class A>, <class object>]

obj = D()
print(obj.method())  # "B" (B находится раньше C в списке)

Diamond Problem

Классическая проблема множественного наследования:

class Shape:
    def area(self):
        return 0

class Rectangle(Shape):
    def area(self):
        return "Rectangle area"

class Triangle(Shape):
    def area(self):
        return "Triangle area"

class RightTriangle(Rectangle, Triangle):
    pass

print(RightTriangle.mro())
# [RightTriangle, Rectangle, Triangle, Shape, object]

obj = RightTriangle()
print(obj.area())  # "Rectangle area" (Rectangle идёт первым)

Python решает Diamond Problem через MRO, гарантируя, что Shape появляется только один раз и в правильном месте.

super() и MRO

Функция super() использует MRO для вызова методов родительских классов:

class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return f"Dog barks. {super().speak()}"

class Puppy(Dog):
    def speak(self):
        return f"Puppy yelps. {super().speak()}"

obj = Puppy()
print(obj.speak())
# "Puppy yelps. Dog barks. Some sound"

print(Puppy.mro())
# [Puppy, Dog, Animal, object]

Сложный пример с множественным наследованием

class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return f"B -> {super().method()}"

class C(A):
    def method(self):
        return f"C -> {super().method()}"

class D(B, C):
    def method(self):
        return f"D -> {super().method()}"

print(D.mro())
# [D, B, C, A, object]

obj = D()
print(obj.method())
# "D -> B -> C -> A"

Обратите внимание: C всегда идёт после B в цепочке, потому что B был указан первым в D(B, C).

Практические применения MRO

1. Правильное использование super():

class Mixin:
    def initialize(self):
        print("Mixin initialized")
        super().initialize()

class Base:
    def initialize(self):
        print("Base initialized")

class Combined(Mixin, Base):
    def initialize(self):
        print("Combined initialized")
        super().initialize()

obj = Combined()
obj.initialize()
# Combined initialized
# Mixin initialized
# Base initialized

2. Проверка MRO для отладки:

class MyClass(Base1, Base2, Base3):
    pass

# Отладка наследования
for i, cls in enumerate(MyClass.__mro__):
    print(f"{i}: {cls.__name__}")

# Или более детально
import inspect
print(inspect.getmro(MyClass))

Частые ошибки

1. Нарушение MRO:

# ❌ Это вызовет ошибку
class A:
    pass

class B(A):
    pass

class C(A):
    pass

# Неправильный порядок — создаст несогласованную иерархию
try:
    class D(B, A, C):
        pass
except TypeError as e:
    print(f"Error: {e}")

2. Забывчивость super():

# ❌ Неправильно — не вызывает родительский метод
class Child(Parent):
    def method(self):
        # self.method() — бесконечная рекурсия!
        pass

# ✅ Правильно
class Child(Parent):
    def method(self):
        super().method()  # Вызывает Parent.method()

Лучшие практики

  • Избегай глубокого наследования — максимум 3-4 уровня
  • Используй super() вместо явного вызова родительского класса
  • Проверяй MRO для сложных иерархий: ClassName.mro()
  • Предпочитай композицию наследованию когда возможно
  • Используй миксины правильно — поместь их в начало списка базовых классов

Понимание MRO критично для работы с объектно-ориентированным кодом и при отладке проблем наследования в больших проектах.

Как работает MRO? | PrepBro