To super or not to super (Re: Accessing parent objects)

Ian Kelly ian.g.kelly at gmail.com
Tue Mar 27 10:47:37 EDT 2018


On Tue, Mar 27, 2018 at 12:21 AM, Gregory Ewing
<greg.ewing at canterbury.ac.nz> wrote:
> The idea that super() is *always* the right way to call
> inherited methods in a multiple inheritance environment
> seems to have been raised by some people to the level
> of religous dogma.
>
> I don't buy it. In order for it to work, the following
> two conditions must hold:
>
> 1) All the methods involved have "additive" behaviour,
> i.e. the correct thing to do is always to call *all*
> versions of the method with the same arguments.

I would argue that this is just good OO design. Inheritance with
subtractive behavior violates the Liskov substitution principle.

> 2) It doesn't matter what order they're called in.

This is simply not true. The Python MRO is very specific about the
order they will be called in, and it obeys some important rules:

1) Monotonicity - If the MRO of class C has class B1 before B2, then
the MRO of any subclass of C must also have B1 before B2.

2) Local precedence ordering - If C inherits directly from B1 and B2
in that order, then its MRO must have B1 before B2.

The algorithm used is also precisely specified, so in cases where
these rules would permit more than one possible ordering, the actual
MRO can be determined by running the algorithm.

> The trouble is, those conditions don't always hold.
> Often when overriding a method, you want to do something
> *instead* of what the base method does.

As noted above, unless the method or class is abstract then this
violates LSP. If the method is abstract and does nothing, then just
call it. If the method is abstract and raises an exception, then
that's a little more tricky. Ideally, don't write abstract methods
that raise NotImplementedError unless they're not intended to be used
with multiple inheritance.

If you really need to though, you can solve this by creating a guard
class that implements the method and does nothing, ending the super
call chain. Then just make sure that at least one subclass in the
multiple inheritance diagram inherits from the guard class rather than
the original class.

> Or you want to
> do something additional, but the base method must be
> called *first*.

I don't see why this is a problem; you can easily do other things
before calling super().

> On the other hand, explicit inherited method calls give
> you precise control over what happens and what order it
> happens in, with no chance of it being messed up by
> someone ineriting from you.

Unless they create a diamond in the process. Also, note even without
using super, the MRO is still used when deciding which version of an
inherited method to call if the subclass does not directly define it.
So you may end up accidentally creating a class structure that uses
one order for some methods and a different order for others. Using
super ensures that your calls follow the MRO.

> Yes, diamonds can be a problem. You can mitigate that
> by (1) avoiding diamonds;

Which can only be accomplished by not using multiple inheritance at all.

> (2) if you can't avoid
> diamonds, avoid putting methods in the apex class that
> get called as inherited methods;

Then what purpose does the apex class serve?

> (3) if you can't avoid
> either of those, as far as I can tell the situation is
> intractable and you need to rethink your design.

Or you can use super.



More information about the Python-list mailing list