Почему MRO отличается в python 2 и python 3?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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 гарантирует:
- Консистентность: каждый класс ищется только один раз
- Локальность: порядок родителей в определении класса сохраняется
- Монотонность: если 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 убрал старые классы
- Сложность: две системы классов — это путаница
- Непредсказуемость: DFS приводил к ошибкам
- Производительность: одна система быстрее
- Будущее: 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 как единственный стандарт, потому что это более надежный и предсказуемый алгоритм для множественного наследования.