← Все вопросы

Как сделать множественное наследование в Python и что вообще такое MRO?

Задан 9 дней назад288 просмотров2 ответа
2

В книжке прочитал, что Python умеет наследоваться сразу от нескольких классов: class C(A, B). Но не понимаю, что произойдёт, если и в A, и в B есть метод с одним именем — чей вызовется? И постоянно встречаю слово «MRO» и «проблема ромба», но что это значит на практике — не догоняю.

2 ответа

7
✓ Принятый ответ — помог автору

Множественное наследование — это просто перечисление базовых классов через запятую:

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

class B:
    def hello(self):
        print("B")

class C(A, B):
    pass

C().hello()   # напечатает "A"

Почему A, а не B? Тут вступает в игру MRO (Method Resolution Order) — порядок, в котором Python ищет метод по цепочке классов. Посмотреть его можно так:

print(C.__mro__)
# (C, A, B, object)

Питон идёт слева направо: сначала смотрит в самом C, потом в A, потом в B, и только потом в object. Нашёл hello в A — взял его, дальше не ищет.

Проблема ромба — это когда A и B оба наследуются от общего Base:

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

class A(Base):
    def __init__(self):
        super().__init__()
        print("A")

class B(Base):
    def __init__(self):
        super().__init__()
        print("B")

class C(A, B):
    def __init__(self):
        super().__init__()
        print("C")

C()
# Base / B / A / C

Главное здесь — super() вызывает НЕ «родителя», а следующий класс по MRO. Благодаря алгоритму C3 каждый класс в ромбе вызывается ровно один раз, без дублирования Base. Поэтому правило: всегда используй super(), а не зови родителя по имени (Base.__init__(self)) — иначе ромб сломается.

На практике множественное наследование чаще всего используют для миксинов — маленьких классов-добавок (LoggerMixin, SerializableMixin), которые подмешивают функциональность, не строя глубокую иерархию.

0

Добавлю предостережение: множественным наследованием легко выстрелить себе в ногу. Если иерархия запутанная, MRO становится неочевидным, и super() начинает «прыгать» туда, куда не ждёшь.

Практичный совет — держи миксины «тонкими» и обязательно прокидывай *args, **kwargs в super().__init__(), иначе классы в цепочке не получат свои аргументы:

class Mixin:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

И если Python вдруг ругается TypeError: Cannot create a consistent method resolution order — значит ты задал классы в таком порядке, что согласованный MRO невозможен. Поменяй порядок базовых классов местами.

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект