python multiple inheritance passing arguments to constructors using super
Solution 1:
Well, when dealing with multiple inheritance in general, your base classes (unfortunately) should be designed for multiple inheritance. Classes B
and C
in your example aren't, and thus you couldn't find a proper way to apply super
in D
.
One of the common ways of designing your base classes for multiple inheritance, is for the middle-level base classes to accept extra args in their __init__
method, which they are not intending to use, and pass them along to their super
call.
Here's one way to do it in python:
class A(object):
def __init__(self,a):
self.a=a
class B(A):
def __init__(self,b,**kw):
self.b=b
super(B,self).__init__(**kw)
class C(A):
def __init__(self,c,**kw):
self.c=c
super(C,self).__init__(**kw)
class D(B,C):
def __init__(self,a,b,c,d):
super(D,self).__init__(a=a,b=b,c=c)
self.d=d
This can be viewed as disappointing, but that's just the way it is.
Solution 2:
Unfortunately, there is no way to make this work using super()
without changing the Base classes. Any call to the constructors for B
or C
is going to try and call the next class in the Method Resolution Order, which will always be B
or C
instead of the A
class that the B
and C
class constructors assume.
The alternative is to call the constructors explicitly without the use of super()
in each class.
class A(object):
def __init__(self, a):
object.__init__()
self.a = a
class B(A):
def __init__(self, a, b):
A.__init__(self, a)
self.b = b
class C(A):
def __init__(self, a, c):
A.__init__(self, a)
self.c = c
class D(B, C):
def __init__(self, a, b, c, d):
B.__init__(self, a, b)
C.__init__(self, a, c)
self.d = d
There is still a downside here as the A
constructor would be called twice, which doesn't really have much of an effect in this example, but can cause issues in more complex constructors. You can include a check to prevent the constructor from running more than once.
class A(object):
def __init__(self, a):
if hasattr(self, 'a'):
return
# Normal constructor.
Some would call this a shortcoming of super()
, and it is in some sense, but it's also just a shortcoming of multiple inheritance in general. Diamond inheritance patterns are often prone to errors. And a lot of the workarounds for them lead to even more confusing and error-prone code. Sometimes, the best answer is to try and refactor your code to use less multiple inheritance.
Solution 3:
A key concept: super does not refer to the parent class. It refers to the next class in the mro list, which depends on the actual class being instantiated.
So when calling super().__init__
, the actual method called is undetermined from the calling frame.
That's why the classes have to be specially designed for mixin.
Even a class witch inherits only from object
, should call super().__init__
.
And of course, when object__init__(**kwargs)
is called, kwargs
should be empty by then; else case an error will raise.
Example:
class AMix:
def __init__(self, a, **kwargs):
super().__init__(**kwargs)
self.a = a
class BMix:
def __init__(self, b, **kwargs):
super().__init__(**kwargs)
self.b = b
class AB(AMix, BMix):
def __init__(self, a, b):
super().__init__(a=a, b=b)
ab = AB('a1', 'b2')
print(ab.a, ab.b) # -> a1 b2