Что такое ромбовидное наследование
Что проверяют
Понимаете ли вы, как Python разрешает множественное наследование, или пересказываете проблему из C++. Вопрос почти всегда ведёт к порядку разрешения методов, и по ответу видно, писал ли человек миксины.
Как отвечать
Ромб это ситуация, когда класс наследует двум классам с общим предком. В Python ромб есть всегда: любой класс наследует object, поэтому вопрос не «как избежать», а «в каком порядке ищется атрибут».
Порядок задаёт MRO, линеаризация по алгоритму C3: сохраняется порядок баз в объявлении, потомок всегда стоит раньше предка, общий предок попадает в список один раз и в конце. Посмотреть можно у самого класса, атрибутом mro.
class A:
def go(self): return "A"
class B(A):
def go(self): return "B" + super().go()
class C(A):
def go(self): return "C" + super().go()
class D(B, C): pass
D().go() # BCA: super в B ведёт в C, а не в AГлавное следствие: super() идёт не к родителю класса, где он написан, а к следующему по MRO конкретного объекта. Поэтому общий предок вызывается ровно один раз. Это и есть кооперативное наследование: каждый метод в цепочке зовёт super(), а не конкретный класс. Если порядок баз в разных ветках противоречив, интерпретатор откажется создать класс с ошибкой о несогласованном MRO.
Где спотыкаются
Вызывают метод через имя класса, а не через super(). Цепочка рвётся: общий предок отрабатывает дважды или пропускается целиком, и поведение меняется от того, кого унаследовали первым.
Переносят проблему из C++ и рассказывают про дублирование подобъекта базы и виртуальное наследование. В Python экземпляр один, состояние не дублируется, и вся сложность сводится к порядку разрешения имён.
Пишут миксины с несовместимыми сигнатурами конструктора и не передают лишние именованные аргументы дальше по цепочке. Пока миксин один, всё работает; в ромбе инициализация обрывается на первом же несовпадении, и объект получается наполовину настроенным.