More arguments in derived class __init__ than base class __init__
<p>How can I extend the <code>__init__</code> of a base class, <strong>add more arguments to be parsed</strong>, without requiring <code>super().__init__(foo, bar)</code> in every derived class?</p>

<pre class="lang-python prettyprint-override"><code>class Ipsum:
    """ A base ipsum instance. """

    def __init__(self, foo, bar):
        self.foo = flonk(foo)
        grungole(self, bar)
        self._baz = make_the_baz()


class LoremIpsum(Ipsum):
    """ A more refined ipsum that also lorems. """

    def __init__(self, foo, bar, dolor, sit, amet):
        super().__init__(foo, bar)
        farnark(sit, self, amet)
        self.wibble(dolor)
</code></pre>

<p>The purpose of the example is to show that there is significant processing happening in the <code>Ipsum.__init__</code>, so it should not be duplicated in every sub-class; and the <code>LoremIpsum.__init__</code> needs the <code>foo</code> and <code>bar</code> parameters processed exactly the same as <code>Ipsum</code>, but also has its own special parameters.</p>

<p>It also shows that, if <code>Ipsum</code> needs to be modified to accept a different signature, <em>every derived class</em> also needs to change not only its signature, but how it calls the superclass <code>__init__</code>. That's unacceptably fragile.</p>

<p>Instead, I'd like to do something like:</p>

<pre class="lang-python prettyprint-override"><code>class Ipsum:
    """ A base ipsum instance. """

    def __init__(self, foo, bar, **kwargs):
        self.foo = flonk(foo)
        grungole(self, bar)
        self._baz = make_the_baz()

        self.parse_init_kwargs(kwargs)

    def parse_init_kwargs(self, kwargs):
        """ Parse the remaining kwargs to `__init__`. """
        pass


class LoremIpsum(Ipsum):
    """ A more refined ipsum that also lorems. """

    def parse_init_kwargs(self, kwargs):
        (dolor, sit, amet) = (kwargs['dolor'], kwargs['sit'], kwargs['amet'])
        farnark(sit, self, amet)
        self.wibble(dolor)
</code></pre>

<p>That has the big advantage that <code>LoremIpsum</code> need <em>only</em> do the parts that are special to that class; handling <code>Ipsum</code> arguments is handled by that class's <code>__init__</code> without any extra code.</p>

<p>The disadvantage is obvious, though: this is effectively re-implementing the handling of named parameters by passing a dictionary around. It avoids a lot of repetition, but isn't very clear.</p>

<p>What tools are available to avoid the sub-class definitions <em>always</em> needing to declare the <code>foo</code> and <code>bar</code> parameters, and <em>always</em> needing to call <code>super().__init__(foo, bar)</code>? Those are easy to get wrong, so it would be better if they weren't needed and could <strong>just automatically happen</strong>, while still allowing <code>LoremIpsum</code>'s customisation of the initialisation.</p>

<p>The usual way to write these is roughly as follows:</p>

<pre><code>class Ipsum:  # 3.x-ism; in 2.x always inherit from object.
    def __init__(self, arg1, arg2, arg3):
        # etc...

class LoremIpsum(Ipsum):
    def __init__(self, arg4, arg5, *args, **kwargs):
        super().__init__(*args, **kwargs)  # 3.x-ism; in 2.x super() needs arguments.
        # Do stuff with arg4 and arg5
</code></pre>

<p>This does not require modifying derived classes when the base class changes signatures.  You will still need to modify everything that directly instantiates your base class, so changing the signature is still not something you want to do frequently.</p>

<p>This approach is also superior because it will generally behave more or less correctly in the case of multiple inheritance, even if your derived class gets put in front of another derived class in the method resolution order and you've never heard of that other class (in other words, even if <code>super().__init__()</code> is calling a method you know nothing about).  This is discussed in detail in Hettinger's <a href="https://rhettinger.wordpress.com/2011/05/26/super-considered-super/" rel="nofollow">super() considered super</a> blog post.</p>

python inheritance