[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