Непосредственная проблема с вашим кодом заключается в том, что ClassC
наследуется от GraphicsWindow
в качестве первого базового класса, а ClassA
- второго базового класса. Когда вы вызываете super
, вызывается только один (GraphicsWindow
), и если он не предназначен для работы с множественным наследованием (как это имеет место), он может не вызывать super
сам по себе или может не передавать аргументы, которые ClassA
ожидает.
Простого переключения порядка базовых классов может быть достаточно, чтобы заставить его работать. Python гарантирует, что базовые классы будут вызываться в том же относительном порядке, в котором они появляются в операторе class
(хотя другие классы могут быть вставлены между ними в MRO, если позднее произойдет больше наследования). Поскольку ClassA.__init__
вызывает super
, он должен работать лучше!
Может быть сложно заставить методы __init__
работать с множественным наследованием, даже если все задействованные классы предназначены для работы с ним. Вот почему позиционных аргументов часто избегают, поскольку их порядок может привести к путанице (поскольку дочерние классы могут добавлять позиционные аргументы только перед позиционными аргументами своих родителей, если они не хотят повторять все имена). Использование ключевых аргументов, безусловно, является лучшим подходом.
Но код, который у вас есть, делает работу с аргументами ключевых слов немного сложнее, чем должно быть. Вам не нужно явно создавать словари для передачи с синтаксисом **kwargs
, а также не нужно извлекать значения ключевых слов из условия, принятого вами с аргументом **kwargs
. Обычно каждая функция должна называть аргументы, которые она принимает, и использовать **kwargs
только для неизвестных аргументов (которые могут понадобиться другому классу в MRO). Вот как это выглядит:
class Base1:
def __init__(self, *, arg1, arg2, arg3, **kwargs): # the * means the other args are kw-only
super().__init__(**kwargs) # always pass on all unknown arguments
... # use the named args here (not kwargs)
class Base2:
def __init__(self, *, arg4, arg5, arg6, **kwargs):
super().__init__(**kwargs)
...
class Derived(Base1, Base2):
def __init__(self, *, arg2, arg7, **kwargs): # child can accept args used by parents
super().__init__(arg2=arg2+1, arg6=3, **kwargs) # it can then modify or create from
... # scratch args to pass to its parents
obj = Derived(arg1=1, arg2=2, arg3=3, arg4=4, arg5=5, arg7=7) # note, we've skipped arg6
# and Base1 will get 3 for arg2
Но я бы также серьезно отнесся к тому, имеет ли наследство смысл в вашей ситуации. Для одного из двух базовых классов может иметь смысл инкапсулироваться в дочерний класс, а не наследоваться от. То есть вы наследуете только от одного из ClassA
или GraphicsWindow
и сохраняете экземпляр другого в каждом экземпляре ClassC
. (Вы могли бы даже наследовать ни от одного базового класса, и инкапсулировать их оба.) Инкапсуляцию часто намного проще рассуждать и получать права, чем наследование.