OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.
For:
class C:
def __init__(self, arg1=1):
self.attr1 = arg1
class D(C):
def __init__(self, arg1):
super().__init__()
This would then yield:
d = D(2)
print(d.attr1) # prints: 1
Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with its default value.
Or to quote the docs for what super does:
Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.
I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So your example would actually not work, because both parents' constructors expect an instance and two more arguments.
In your example you could say:
class A:
def __init__(self, x1, x2):
print(f"Entering A [x1={x1}, x2={x2}]")
print("Leaving A")
class B:
def __init__(self, y1, y2):
print(f"Entering B [y1={y1}, y2={y2}]")
print("Leaving B")
class C(A, B):
def __init__(self, x1, y1, x2, y2):
print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
A.__init__(self, x1, x2)
B.__init__(self, y1, y2)
print("Leaving C")
Because you call each specific parent constructors with corresponding specific arguments. The documentation (same place, few paragraph down on second common use case with multiple inheritance) also contains hint regarding that:
Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).
That is, you get a proxy to call the method... and you call it using its arguments... which has to be a fixed / stable set as you cannot be sure up front which method are you reaching through that proxy.