How can I extend the __init__ of a base class, add more arguments to be parsed, without requiring super().__init__(foo, bar) in every derived class?
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)
The purpose of the example is to show that there is significant processing happening in the Ipsum.__init__, so it should not be duplicated in every sub-class; and the LoremIpsum.__init__ needs the foo and bar parameters processed exactly the same as Ipsum, but also has its own special parameters.
It also shows that, if Ipsum needs to be modified to accept a different signature, every derived class also needs to change not only its signature, but how it calls the superclass __init__. That's unacceptably fragile.
Instead, I'd like to do something like:
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)
That has the big advantage that LoremIpsum need only do the parts that are special to that class; handling Ipsum arguments is handled by that class's __init__ without any extra code.
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.
What tools are available to avoid the sub-class definitions always needing to declare the foo and bar parameters, and always needing to call super().__init__(foo, bar)? Those are easy to get wrong, so it would be better if they weren't needed and could just automatically happen, while still allowing LoremIpsum's customisation of the initialisation.