python multi inheritance with parent classes have different __init__()
<p>Here both <code>B</code> and <code>C</code> are derived from <code>A</code>, but with different <code>__init__()</code> parameters. My question is how to write the correct/elegant code here to initialize self.a,self.b,self.c1,self.c2 in the following example?
Maybe another question is--is it a good coding practice to do this variable setting in <code>__init()__</code> function or it is better to use simpler <code>__init__()</code> function, and do <code>set()</code> function for each class later, which seems not as simple as to just do it in <code>__init()__</code>?</p>

<pre class="lang-py prettyprint-override"><code>class A(object):
    __init__(self,a):
        self.a=a
class B(A):
    __init__(self,a,b):
        super(B,self).__init__(a)
        self.b=b
class C(A):
    __init__(self,a,c1,c2):
        super(C,self).__init__(a)
        self.c1=c1
        self.c2=c2

class D(B,C)
    __init__(self,a,b,c1,c2,d):
        #how to write the correct/elegant code here to initialize self.a,self.b,self.c1,self.c2?
        #can I use super(D,self) something?
        self.d=d
        self.dd=self.a+self.b+2*self.c1+5*self.c2+3*self.d

d=D(1,2,3,4,5)   
</code></pre>

<p>Multiple inheritance in Python requires that all the classes cooperate to make it work. In this case, you can make them cooperate by having the <code>__init__</code> method in each class accept arbitrary <code>**kwargs</code> and pass them on when they call <code>super().__init__</code>.</p>

<p>For your example class hierarchy, you could do something like this:</p>

<pre><code>class A(object):
    __init__(self,a):  # Don't accept **kwargs here! Any extra arguments are an error!
        self.a=a

class B(A):
    __init__(self, b, **kwargs):  # only name the arg we care about (the rest go in **kwargs)
        super(B, self).__init__(**kwargs)  # pass on the other keyword args
        self.b=b

class C(A):
    __init__(self, c1, c2, **kwargs):
        super(C,self).__init__(**kwargs)
        self.c1=c1
        self.c2=c2

class D(B,C)
    __init__(self, d, **kwargs):
        super(D,self).__init__(**kwargs)
        self.d=d
        self.dd=self.a+self.b+2*self.c1+5*self.c2+3*self.d
</code></pre>

<p>Note that if you wanted <code>D</code> to use the argument values directly (rather than using <code>self.a</code>, etc.), you could both take them as named arguments and still pass them on in the <code>super()</code> call:</p>

<pre><code>class D(B,C)
    __init__(self,a, b, c1, c2, d, **kwargs): # **kwargs in case there's further inheritance
        super(D,self).__init__(a=a, b=b, c1=c1, c2=c2, **kwargs)
        self.d = d
        self.dd = a + b + 2 * c1 + 5 * c2 + 3 * d   # no `self` needed in this expression!
</code></pre>

<p>Accepting and passing on some args is important if some of the parent classes don't save the arguments (in their original form) as attributes, but you need those values. You can also use this style of code to pass on modified values for some of the arguments (e.g. with <code>super(D, self).__init__(a=a, b=b, c1=2*c1, c2=5*c2, **kwargs)</code>).</p>

<p>This kind of collaborative multiple inheritance with varying arguments is almost impossible to make work using positional arguments. With keyword arguments though, the order of the names and values in a call doesn't matter, so it's easy to pass on named arguments and <code>**kwargs</code> at the same time without anything breaking. Using <code>*args</code> doesn't work as well (though recent versions of Python 3 are more flexible about how you can call functions with <code>*args</code>, such as allowing multiple unpackings in a single call: <code>f(*foo, bar, *baz)</code>).</p>

<p>If you were using Python 3 (I'm assuming not, since you're explicitly passing arguments to <code>super</code>), you could make the arguments to your collaborative functions "keyword-only", which would prevent users from getting very mixed up and trying to call your methods with positional arguments. Just put a bare <code>*</code> in the argument list before the other named arguments: <code>def __init__(self, *, c1, c2, **kwargs):</code>.</p>

python multiple-inheritance