[Tutor] Inheritance, superclass, ‘super’ (was: __repr__ and __str__)

Steven D'Aprano steve at pearwood.info
Thu Jul 2 17:33:25 CEST 2015


I mostly agree with what Ben says, comments below.

On Wed, Jul 01, 2015 at 12:48:47PM +1000, Ben Finney wrote:
> Alan Gauld <alan.gauld at btinternet.com> writes:
> 
> > Whilst I agree with the general use of super I'm not sure what
> > the MRO resolution has to do with this case?
> 
> When accessing the superclass, the MRO is always relevant.

For those who aren't sure, MRO is "Method Resolution Order".

To be precise, all (new style) classes have a MRO:

py> object.__mro__
(<type 'object'>,)

In the multiple-inheritance world, all inheritance is supposed to follow 
the MRO, at least by default. It should be quite unusual to manually 
bypass the MRO or invent your own, that should stand out as a code 
smell.

One complication in Python 2 is that "classic classes" (old style 
classes) don't have an explicit MRO. But they do have an implicit one. 
Unfortunately classic classes' MRO is buggy when used with multiple 
inheritance with a diamond-shaped class hierachy. I won't explain that 
unless somebody asks, the important thing to take from this is that you 
should try to avoid inheriting from "classic classes" in Python 2.

 
> > It's explicitly single inheritance and they are explicitly calling the
> > superclass.
> 
> They don't know that the superclass will be what is written there;
> that's an implication of what I said with “in Python, any class an
> participate in multiple inheritance”.
> 
> The author of a class (e.g. the OP's example ‘MyList’) cannot know at
> time of writing whether the superclass is ‘list’. This is because some
> other class may be using ‘MyList’ in a different inheritance
> arrangement.

In principle, Ben is correct here, but in practice, multiple-inheritance 
in Python is explicitly described as *cooperative* multiple-inheritance. 
You cannot expect to take any two (or more) arbitrary classes and 
inherit from them both. Sometimes you cannot inherit from them both at 
all:

>>> class X(list, int):
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: multiple bases have instance lay-out conflict


Sometimes you cannot inherit from them both because there is no 
consistent MRO, and sometimes you cannot inherit from them both because 
they simply don't work well together. In this last case, Python cannot 
warn you about it, but your class will just not work right. ("Your save 
method over-writes the data my save method just wrote!" sort of thing.)

The simplest way to break M-I is to directly call your parent class by 
name, instead of using super(). That effectively guarantees that your 
class cannot be used in M-I, except perhaps by accident.

In a nutshell, you can physically inherit from "the" parent class by 
explicitly giving the name of the class, but by doing so you are 
effectively saying "Uh uh, oh no you don't, no way!" to any one wishing 
to use your class in a multiple inheritance situation. As the writer of 
the class, that is your right to do so (nobody can force you to write 
cooperative M-I classes), but in my opinion it's a bit rude or ignorant, 
like writing functions that never return their result but always print 
it instead. ("Nobody's going to re-use *my* functions, uh uh, no way!")

(In the M-I world, there is no such thing as "the" parent class. There 
is, however, the parent *classes*.)
 
[...]
> Multiple inheitance is a fact in Python, and good practice is to not
> arbitrarily write classes that break it. Hence my advice to avoid
> hard-coding the superclass, and only use ‘super’ to discover the
> superclass.

I agree that it is good practice to use super, even if you personally 
expect to only use single-inheritance. You never know when somebody 
else, or even your future self, will decide to use M-I. Don't 
unnecessarily cut off that possibility, especially in Python 3 where 
using super() is actually easier than explicitly calling the parent 
class by hand:

    list.append(self, arg)

    super().append(arg)


On the other hand, M-I in Python is cooperative, and it's not very often 
that you can expect to pick two arbitrary classes and inherit from them 
both.

And on the third hand, inheritance in general is often over-rated, and 
M-I in particular is hard to get right. There's a strong argument to be 
made that one should only use full cooperative M-I when easier, less 
powerful versions like mixins and traits aren't enough. Or use 
composition and/or delegation instead.


>     So please use `super`, even in single inheritance. Otherwise you are
>     restricting the usefulness of your class: it can never be used with
>     multiple inheritance.

I cannot disagree with Ben here. Even if you only intend to use single 
inheritance, don't unnecessarily rule out the alternatives. Using 
super is still usually the right thing to do.


-- 
Steve


More information about the Tutor mailing list