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

Почему MRO отличается в python 2 и python 3?

1.0 Junior🔥 111 комментариев
#Асинхронность и многопоточность

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

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

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

MRO (Method Resolution Order) в Python 2 vs Python 3

MRO — это порядок, в котором Python ищет методы и атрибуты в иерархии классов при множественном наследовании. Это критически важно для правильной работы наследования.

Две системы классов в Python

Python 2: Старые и новые классы

Python 2 имел две параллельные системы классов:

# ❌ Старый класс (не наследуется от object)
class OldStyle:
    pass

# ✅ Новый класс (явное наследование от object)
class NewStyle(object):
    pass

Они использовали разные MRO алгоритмы:

  • Старые классы: Depth-First Search (DFS)
  • Новые классы: C3 Linearization

Python 3: Единая система

Python 3 убрал старые классы. Все классы теперь наследуются от object по умолчанию и используют C3 Linearization.

# Python 3: ALL классы новые (неявное наследование от object)
class AnyClass:
    pass

# Эквивалентно:
class AnyClass(object):
    pass

Алгоритм DFS (Depth-First Search) — Python 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):
    pass

# Python 2 old-style MRO (DFS):
# D -> B -> A -> C -> A
#         ^ повторение A!

# Проблема: A вызывается дважды, нарушается консистентность

Алгоритм C3 Linearization — Python 2 новые классы и Python 3

C3 гарантирует:

  1. Консистентность: каждый класс ищется только один раз
  2. Локальность: порядок родителей в определении класса сохраняется
  3. Монотонность: если A появляется раньше B в MRO одного класса, то A раньше B везде
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

# Python 3 MRO (C3 Linearization):
# D -> B -> C -> A
#      ^ линейный, консистентный, A только один раз

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

Конкретный пример, где DFS ломается

# Python 2 old-style класс
class A:
    def greet(self):
        print("Hello from A")

class B(A):
    def greet(self):
        print("Hello from B")
        A.greet(self)  # Явный вызов супер-класса

class C(A):
    def greet(self):
        print("Hello from C")
        A.greet(self)

class D(B, C):
    pass

d = D()
d.greet()

# Python 2 old-style MRO (DFS): D -> B -> A -> C
# Результат:
# Hello from B
# Hello from A
# A.greet(self) в C — не выполнится!
# C.greet() вообще не вызовется

# ❌ ПРОБЛЕМА: C пропущен, логика нарушена!

С C3 Linearization эта проблема исчезает:

# Python 3 MRO (C3): D -> B -> C -> A
# Правильный порядок:
# Hello from B
# Hello from C (через super())
# Hello from A

Почему был выбран C3

C3 Linearization был создан специально для Python, чтобы решить проблемы DFS:

DFS проблемы:

  • 🔴 Класс может быть вызван несколько раз
  • 🔴 Нарушается порядок, указанный программистом
  • 🔴 Непредсказуемое поведение в сложных иерархиях

C3 преимущества:

  • ✅ Каждый класс в MRO только один раз
  • ✅ Порядок родителей сохраняется
  • ✅ Консистентное и предсказуемое поведение
  • ✅ Работает даже для сложного множественного наследования

Как посмотреть 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__
print(D.__mro__)
# (<class D>, <class B>, <class C>, <class A>, <class object>)

# Способ 3: через help()
help(D)
# Покажет Method Resolution Order

Super() работает благодаря C3

class A:
    def process(self):
        print("A processing")

class B(A):
    def process(self):
        print("B processing")
        super().process()  # Следует MRO

class C(A):
    def process(self):
        print("C processing")
        super().process()

class D(B, C):
    def process(self):
        print("D processing")
        super().process()

d = D()
d.process()
# Output:
# D processing
# B processing
# C processing
# A processing

# super() автоматически следует MRO (D -> B -> C -> A)

Проблемы, которые исправил C3

Diamond Problem

    A
   / \
  B   C
   \ /
    D

# Кто вызывается при D.method()?
# DFS (Python 2): B -> A -> C -> A (A дважды!) ❌
# C3 (Python 3): B -> C -> A (A один раз) ✅

Почему Python 3 убрал старые классы

  1. Сложность: две системы классов — это путаница
  2. Непредсказуемость: DFS приводил к ошибкам
  3. Производительность: одна система быстрее
  4. Будущее: C3 более гибкая для эволюции языка

Практический совет

# Всегда используй super() вместо явных вызовов

# ❌ Старый стиль (Python 2)
class B(A):
    def method(self):
        A.method(self)  # Жёсткая привязка

# ✅ Новый стиль (Python 3, работает с MRO)
class B(A):
    def method(self):
        super().method()  # Следует MRO автоматически

В итоге: Python 3 выбрал C3 Linearization как единственный стандарт, потому что это более надежный и предсказуемый алгоритм для множественного наследования.