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

В чем разница между поиском родителя в Python 2 и Python 3?

2.2 Middle🔥 81 комментариев
#Python Core

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

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

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

# Разница между поиском родителей (MRO) в Python 2 и Python 3

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

Python 2: Depth-First Search (DFS)

В Python 2 использовался алгоритм Depth-First Search (поиск в глубину), который создавал проблемы с множественным наследованием.

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

# Python 2
class A(object):
    def method(self):
        return "A"

class B(A):
    pass

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

class D(B, C):
    pass

d = D()
print(d.method())  # В Python 2 вернёт "A" !

Порядок поиска в Python 2: D → B → A → C → A

Это видно из MRO:

# Python 2
print(D.__mro__)
# (D, B, A, C, A)  # Заметь A повторяется!

Проблема DFS:

# Python 2 — классическая проблема
class Base(object):
    def method(self):
        return "Base"

class Left(Base):
    def method(self):
        return "Left"

class Right(Base):
    def method(self):
        return "Right"

class Child(Left, Right):
    pass

child = Child()
print(child.method())  # Python 2: "Left"
print(Child.__mro__)  # (Child, Left, Base, Right, Base)
                      # Base посещается дважды!

Python 3: C3 Linearization

В Python 3 используется C3 линеаризация (разработана для Lisp), которая решает проблемы DFS.

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

# Python 3
class A(object):
    def method(self):
        return "A"

class B(A):
    pass

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

class D(B, C):
    pass

d = D()
print(d.method())  # Python 3 вернёт "C"

Порядок поиска в Python 3: D → B → C → A

Это видно из MRO:

# Python 3
print(D.__mro__)
# (<class D>, <class B>, <class C>, <class A>, <class object>)
# Каждый класс посещается ОДИН раз

Принципы C3:

  1. Каждый класс посещается ровно один раз
  2. Порядок аргументов сохраняется — если вы указали D(B, C), то B ищется перед C
  3. Родители родителей ищутся в конце — Base должен быть последним, не первым

Практический пример: реальная проблема

# Python 2 vs Python 3
class Database:
    def connect(self):
        return "Database.connect()"

class Logger:
    def connect(self):
        return "Logger.connect()"

class Service(Database, Logger):
    pass

service = Service()
print(service.connect())

# Python 2: Database.connect() (поиск в глубину)
# Python 3: Database.connect() (C3 линеаризация, слева направо)

class Service2(Logger, Database):
    pass

service2 = Service2()
print(service2.connect())

# Python 2: Logger.connect() (поиск в глубину)
# Python 3: Logger.connect() (C3 линеаризация, слева направо)

Сложный пример: Diamond Pattern (ромбовидное наследование)

Это классическое тестовое задание для MRO:

class Base:
    def method(self):
        return "Base"
    
    def display(self):
        print(f"Base: {self.__class__.__name__}")

class Left(Base):
    def method(self):
        return "Left"

class Right(Base):
    def method(self):
        return "Right"

class Child(Left, Right):
    pass

# Диаграмма наследования:
#     Base
#    /    \
#  Left  Right
#    \   /
#    Child

child = Child()
print(Child.__mro__)

# Python 2:
# (Child, Left, Base, Right, Base)  ❌ Base дважды!
# MRO: Child -> Left -> Base -> Right -> Base -> object

# Python 3:
# (<class Child>, <class Left>, <class Right>, <class Base>, <class object>)
# ✓ Каждый класс один раз, порядок сохранён

super() — огромная разница

Python 2: Нужно явно передавать класс и self

# Python 2
class Base(object):
    def method(self):
        return "Base"

class Child(Base):
    def method(self):
        result = super(Child, self).method()  # Нужно передавать параметры!
        return result + " + Child"

child = Child()
print(child.method())  # Base + Child

Python 3: Просто super() без параметров

# Python 3
class Base:
    def method(self):
        return "Base"

class Child(Base):
    def method(self):
        result = super().method()  # Автоматически узнаёт класс и self!
        return result + " + Child"

child = Child()
print(child.method())  # Base + Child

Сложные цепочки с super()

# Python 3 — красивый код благодаря C3
class A:
    def method(self):
        print("A.method()")
        super().method()

class B(A):
    def method(self):
        print("B.method()")
        super().method()

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

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

d = D()
d.method()
# Вывод:
# D.method()
# B.method()
# C.method()
# A.method()

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

# В Python 2 это бы сломалось из-за DFS

Таблица сравнения

ПараметрPython 2 (DFS)Python 3 (C3)
АлгоритмDepth-First SearchC3 Linearization
Повторение классовВозможноКаждый класс один раз
ПорядокНепредсказуемСлева направо, родители в конце
super()super(Class, self)super()
Diamond patternПроблемыРаботает правильно
Множественное наследованиеХрупкоНадежно

Визуализация MRO

# Python 3
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
class E(D): pass

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

# Диаграмма:
#       object
#         |
#         A
#        / \
#       B   C
#        \ /
#         D
#         |
#         E
#
# MRO: E -> D -> B -> C -> A -> object
#      (слева направо, родители в конце)

Практический совет: Как отладить MRO

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

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

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

class C(A, B):
    pass

# Просмотр MRO
print(C.__mro__)  # Порядок поиска
print(C.mro())    # Альтернативный способ

# Используй inspect для красивого вывода
from inspect import getmro
for cls in getmro(C):
    print(f"  {cls.__name__}")
# Вывод:
#   C
#   A
#   B
#   Base
#   object

Почему Python 3 лучше

  1. Предсказуемость — MRO всегда логичен
  2. Безопасность — нет дублирования в цепочке
  3. Производительность — не нужно проверять дублирующиеся классы
  4. super() удобнее — не нужно передавать параметры
  5. Множественное наследование работает — можно спокойно его использовать

Итог

C3 линеаризация в Python 3 — это огромный шаг вперед в правильности и предсказуемости множественного наследования. Python 2 уже не поддерживается (EOL в 2020 году), поэтому все новые проекты используют C3. Это один из немногих случаев, где язык был кардинально улучшен без обратной совместимости.

В чем разница между поиском родителя в Python 2 и Python 3? | PrepBro