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

Что такое Method Resolution Order (MRO)?

2.0 Middle🔥 201 комментариев
#DevOps и инфраструктура#Django

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

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

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

Method Resolution Order (MRO)

Что такое MRO?

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

Это особенно важно при использовании множественного наследования, когда класс наследует от нескольких родителей.

1. Простое наследование (линейное)

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

class Dog(Animal):
    def speak(self):
        return "Woof!"

class GoldenRetriever(Dog):
    pass  # Наследует speak от Dog

dog = GoldenRetriever()
print(dog.speak())  # "Woof!"

# MRO: [GoldenRetriever, Dog, Animal, object]
# Python ищет методы в этом порядке
print(GoldenRetriever.__mro__)
# (<class 'GoldenRetriever'>, <class 'Dog'>, <class 'Animal'>, <class 'object'>)

Порядок:

  1. Проверяет GoldenRetriever
  2. Проверяет Dog → находит speak()
  3. Возвращает результат

2. Множественное наследование (сложнее!)

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):  # Наследует от B и C
    pass

obj = D()
print(obj.method())  # "B"
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

Почему "B"? Потому что B указан первым в D(B, C).

3. Diamond Problem (классическая проблема)

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

class Dog(Animal):
    def speak(self):
        return "Woof"

class Cat(Animal):
    def speak(self):
        return "Meow"

class Hybrid(Dog, Cat):  # Наследует от Dog и Cat (оба от Animal)
    pass

hybrid = Hybrid()
print(hybrid.speak())  # "Woof"
print(Hybrid.__mro__)
# (<class 'Hybrid'>, <class 'Dog'>, <class 'Cat'>, <class 'Animal'>, <class 'object'>)

Без MRO это привело бы к проблемам:

  • Какой метод вызвать: Dog.speak или Cat.speak или Animal.speak?
  • Animal появляется в цепочке дважды!

MRO решает это: C3 линеаризация гарантирует, что:

  1. Каждый класс появляется только один раз
  2. Родители идут в правильном порядке
  3. Порядок наследования сохраняется

4. C3 Linearization Algorithm

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

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

# Вычисление MRO для D:
# MRO(D) = D + merge(MRO(B), MRO(C), [B, C])
# MRO(B) = [B, A, object]
# MRO(C) = [C, A, object]
#
# merge([B, A, object], [C, A, object], [B, C]):
#   1. B не в хвостах других → берем B
#   2. C не в хвостах → берем C
#   3. A не в хвостах → берем A
#   4. object → берем object
#
# MRO(D) = [D, B, C, A, object]

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

5. super() и MRO

super() использует MRO для вызова методов родителя.

class Animal:
    def speak(self):
        print("I am an animal")

class Dog(Animal):
    def speak(self):
        super().speak()  # Вызывает следующий в MRO
        print("Woof!")

class Beagle(Dog):
    def speak(self):
        super().speak()  # Вызывает следующий в MRO (Dog)
        print("Arooo!")

beagle = Beagle()
beagle.speak()
# I am an animal
# Woof!
# Arooo!

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

Как работает:

  1. beagle.speak() → Beagle.speak()
  2. super().speak() в Beagle → следующий в MRO = Dog → Dog.speak()
  3. super().speak() в Dog → следующий в MRO = Animal → Animal.speak()
  4. Выполняется print в обратном порядке

6. Практический пример: множественное наследование с super()

class Mixin1:
    def method(self):
        print("Mixin1")
        super().method()

class Mixin2:
    def method(self):
        print("Mixin2")
        super().method()

class Base:
    def method(self):
        print("Base")

class Combined(Mixin1, Mixin2, Base):
    pass

obj = Combined()
obj.method()
# Mixin1
# Mixin2
# Base

print(Combined.__mro__)
# (<class 'Combined'>, <class 'Mixin1'>, <class 'Mixin2'>, <class 'Base'>, <class 'object'>)

7. Когда MRO вызывает конфликт

class A: pass
class B(A): pass
class C(A): pass
class D(A): pass

# ❌ ОШИБКА: Inconsistent MRO
try:
    class E(B, C, A, D):  # A появляется и как родитель B, C, D и явно
        pass
except TypeError as e:
    print(e)
    # Cannot create a consistent method resolution order (MRO)
    # for bases B, C, A, D

# ✅ ПРАВИЛЬНО: A только один раз
class E(B, C, D):  # A автоматически в MRO от родителей
    pass

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

8. Просмотр MRO

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

# Способ 1: __mro__ атрибут
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

# Способ 2: __mro__ как список
for cls in D.__mro__:
    print(cls.__name__)
# D
# B
# C
# A
# object

# Способ 3: mro() метод
print(D.mro())
# [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]

9. Проблемы и решения

# ПРОБЛЕМА: Глубокое множественное наследование
class A: pass
class B(A): pass
class C(B): pass
class D(B): pass
class E(C, D): pass
class F(E): pass

print(F.__mro__)
# Сложно отследить!

# РЕШЕНИЕ: Используй composition вместо наследования
class ComponentA:
    def do_something(self):
        pass

class ComponentB:
    def do_something_else(self):
        pass

class Combined:
    def __init__(self):
        self.a = ComponentA()
        self.b = ComponentB()
    
    def do_all(self):
        self.a.do_something()
        self.b.do_something_else()

Правила MRO

  1. Порядок наследования важенclass D(B, C) != class D(C, B)
  2. Каждый класс один раз → A появится только один раз в MRO
  3. Родители сохраняют порядок → MRO(B) идет перед MRO(C) в D(B, C)
  4. Используй super() → Для правильного обхода цепочки методов
  5. Избегай алмазного наследования → Используй Mixin или composition

На собеседовании

Что нужно знать:

  • MRO это порядок поиска методов в иерархии наследования
  • Python использует C3 линеаризацию
  • super() работает с MRO, а не с родительским классом
  • Всегда проверяй .__mro__ или .mro() когда запутался в наследовании
  • Избегай глубокого множественного наследования (лучше composition)