super() will find the next method in the MRO sequence. This means that only one of the __init__ methods in your base classes is going to be called.
You can inspect the MRO (the Method Resolution Order) by looking at the __mro__ attribute of a class:
>>> D.__mro__ (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
so from D, the next class is B, followed by C and object. From D.__init__(), the super().__init__() expression will only call B.__init__(), and then because C.__init__() is never called, self.c is not set either.
You'll have to add more super() calls to your class implementations; it is safe to call object.__init__() with no arguments, so just use them everywhere here:
class B(): def __init__(self): print("__init__ of B called") super().__init__() self.b = "B" class C(): def __init__(self): print("__init__ of C called") super().__init__() self.c = "C" class D(B, C): def __init__(self): print("__init__ of D called") super().__init__() def output(self): print(self.b, self.c)
Now B.__init__ will invoke C.__init__, and C.__init__ will call object.__init__, and calling D().output() works:
>>> d = D() __init__ of D called __init__ of B called __init__ of C called >>> d.output() B C