Как сделать множественное наследование в Python и что вообще такое MRO?
В книжке прочитал, что Python умеет наследоваться сразу от нескольких классов: class C(A, B). Но не понимаю, что произойдёт, если и в A, и в B есть метод с одним именем — чей вызовется? И постоянно встречаю слово «MRO» и «проблема ромба», но что это значит на практике — не догоняю.
2 ответа
Множественное наследование — это просто перечисление базовых классов через запятую:
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), которые подмешивают функциональность, не строя глубокую иерархию.
Добавлю предостережение: множественным наследованием легко выстрелить себе в ногу. Если иерархия запутанная, 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 невозможен. Поменяй порядок базовых классов местами.