super or not super?

Ian Kelly ian.g.kelly at gmail.com
Tue Jul 16 13:55:48 EDT 2019


On Tue, Jul 16, 2019 at 1:21 AM Chris Angelico <rosuav at gmail.com> wrote:
>
> On Tue, Jul 16, 2019 at 3:32 PM Ian Kelly <ian.g.kelly at gmail.com> wrote:
> >
> > Just using super() is not enough. You need to take steps if you want to
> > ensure that you class plays nicely with MI. For example, consider the
> > following:
> >
> > class C1:
> >     def __init__(self, name):
> >         self._name = name
> >
> > class C2(C1):
> >     def __init__(self, name, value):
> >         super().__init__(name)
> >         self._value = value
> >
> > This usage of super is just fine for the single-inheritance shown here.
But
> > there are two reasons why this cannot be neatly pulled into an MI
> > hierarchy. Can you spot both of them?
>
> Well, obviously it's violating LSP by changing the signature of
> __init__, which means that you have to be aware of its position in the
> hierarchy. If you want things to move around smoothly, you HAVE to
> maintain a constant signature (which might mean using *args and/or
> **kwargs cooperatively).

That's pretty close to what I had in mind. Many people treat __init__ as a
constructor (I know, it's not) and so long as you're following that
doctrine it's not really an LSP violation. But anything else that gets
worked into an MI hierarchy has to somehow be compatible with both of these
method signatures while also adding whatever new parameters it needs, which
is a problem. The usual advice for working with this is to use **kwargs
cooperatively as you suggested. *args doesn't work as well because every
class needs to know the absolute index of every positional parameter it's
interested in, and you can't just trim off the end as you go since you
don't know if the later arguments have been consumed yet. With **kwargs you
can just pop arguments off the dict as you go.

On Tue, Jul 16, 2019 at 2:06 AM Antoon Pardon <antoon.pardon at vub.be> wrote:
>
> I guess the second problem is that C1 doesn't call super. Meaning that if
> someone else uses this in a multiple heritance scheme, and the MRO reaches
> C1, the call doesn't get propagated to the rest.

That's it. With single inheritance it's both easy and common to assume that
your base class is object, and since object.__init__ does nothing there's
no point in calling it. But with MI, that assumption isn't valid.



More information about the Python-list mailing list