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>A flexible approach is to have every method in the ancestor tree cooperatively designed to accept keyword arguments and a keyword-arguments dictionary, to remove any arguments that it needs, and to forward the remaining arguments using <code>**kwds</code>, eventually leaving the dictionary empty for the final call in the chain.</p>

<p>Each level strips-off the keyword arguments that it needs so that the final empty dict can be sent to a method that expects no arguments at all (for example, object.__init__ expects zero arguments):</p>

<pre><code>class Shape:
    def __init__(self, shapename, **kwds):
        self.shapename = shapename
        super().__init__(**kwds)        

class ColoredShape(Shape):
    def __init__(self, color, **kwds):
        self.color = color
        super().__init__(**kwds)

cs = ColoredShape(color='red', shapename='circle')
</code></pre>

<p>For more on this approach, see the "Practical Advice" section of the <a href="https://rhettinger.wordpress.com/2011/05/26/super-considered-super/" rel="nofollow">Super Considered Super</a> blogpost, or see the related at <a href="https://www.youtube.com/watch?v=EiOglTERPEo" rel="nofollow">Pycon video</a>.</p>

<p>In your example, the code would look like this:</p>

<pre><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, dolor, sit, amet, **kwds):
        super().__init__(**kwds)
        farnark(sit, self, amet)
        self.wibble(dolor)
</code></pre>

<p>An instantiation would look like this:</p>

<pre><code>li = LoremIpsum(dolor=1, sit=2, amet=3, foo=4, bar=5) 
</code></pre>

<p>Note, this lets you achieve your original objective of adding new arguments to either __init__ method without affecting the other. </p>

python inheritance