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

Как реализован поиск классов в множественном наследовании в Python?

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

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

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

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

Поиск классов в множественном наследовании (MRO)

MRO (Method Resolution Order) — это порядок поиска методов и атрибутов в иерархии классов. Python использует алгоритм C3 Linearization для определения этого порядка.

1. История подходов

DFS (Depth-First Search) - старый подход (Python 2)

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

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

# Python 2 использовал DFS: D -> B -> A -> C -> A (неправильно!)
# A посещается дважды

C3 Linearization - новый подход (Python 3)

Питон 3 использует более умный алгоритм, который гарантирует:

  1. Порядок соответствует объявлению - левые родители до правых
  2. Родитель после детей
  3. Монотонность - порядок предков каждого класса сохраняется

2. Как работает C3 Linearization

Простой пример

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

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

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

3. Использование super()

super() работает в контексте MRO:

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

class B(A):
    def method(self):
        print("B")
        super().method()  # Идет по MRO

class C(A):
    def method(self):
        print("C")
        super().method()  # Идет по MRO

class D(B, C):
    def method(self):
        print("D")
        super().method()  # Идет по MRO

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

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

4. Diamond Problem

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

class Dog(Animal):
    def speak(self):
        print("Woof!")
        super().speak()

class Cat(Animal):
    def speak(self):
        print("Meow!")
        super().speak()

class DogCat(Dog, Cat):  # Множественное наследование
    pass

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

obj = DogCat()
obj.speak()
# Output:
# Woof!
# Meow!
# Some sound

C3 гарантирует, что Animal будет вызван только один раз, в конце.

5. Несовместимые порядки

class X: pass
class Y: pass
class A(X, Y): pass
class B(Y, X): pass
class C(A, B): pass  # TypeError!

# TypeError: Cannot create a consistent method resolution order (MRO)
# for bases Y, X

Это происходит, когда порядки наследования противоречат друг другу.

6. Сложный пример

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

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

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

class D(A):
    def who(self):
        return "D"

class E(B, C):
    pass

class F(C, D):
    pass

class G(E, F):
    pass

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

obj = G()
print(obj.who())  # "B" - первый в MRO, который реализует метод

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

def print_mro(cls):
    print(f"MRO для {cls.__name__}:")
    for i, parent in enumerate(cls.mro()):
        print(f"  {i}: {parent}")

class Shape:
    pass

class Color:
    pass

class Drawable(Shape, Color):
    pass

print_mro(Drawable)
# MRO для Drawable:
#   0: <class 'Drawable'>
#   1: <class 'Shape'>
#   2: <class 'Color'>
#   3: <class 'object'>

8. Django Model Mixin

class TimestampMixin:
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

class AuthorMixin:
    author = models.ForeignKey(User, on_delete=models.CASCADE)

class Post(TimestampMixin, AuthorMixin, models.Model):
    title = models.CharField(max_length=200)

print(Post.mro())
# [Post, TimestampMixin, AuthorMixin, Model, ..., object]

9. Инструменты для анализа

# Просмотр MRO
print(MyClass.__mro__)      # Кортеж
print(MyClass.mro())        # Список

# Проверка наследования
issubclass(Child, Parent)
isinstance(obj, Parent)

# Получить следующий класс в MRO
next_class = super()

# inspect модуль
import inspect
print(inspect.getmro(MyClass))

Ключевые моменты

  1. C3 Linearization - алгоритм поиска в MRO
  2. super() - идет по MRO, а не по наследованию
  3. Порядок наследования важен - class D(B, C) не равен class D(C, B)
  4. Diamond Problem решён - благодаря C3
  5. Несовместимые порядки приводят к ошибке - Python проверяет корректность
  6. MRO видно через .mro() - всегда можно посмотреть порядок
  7. Используй super() - не вызывай методы родителей напрямую

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