Builtn super() function. How to use it with multiple inheritance? And why should I use it at all?

Carl Banks pavlovevidence at gmail.com
Sat Jul 31 23:48:40 EDT 2010


On Jul 31, 11:16 am, Ian Kelly <ian.g.ke... at gmail.com> wrote:
> On Sat, Jul 31, 2010 at 4:22 AM, Steven D'Aprano
>
>
>
> <st... at remove-this-cybersource.com.au> wrote:
> > On Fri, 30 Jul 2010 21:40:21 -0600, Ian Kelly wrote:
>
> >> I have to chime in and agree that the name "super" is problematic. I'm
> >> reading this thread with a sense of alarm because I apparently never
> >> read the super() documentation too closely (why would I?  "Oh, it just
> >> accesses an attribute from a superclass.  Moving on.") and have been
> >> writing code for the past four years under the impression that super()
> >> will always refer to a superclass of the current class.
>
> > In Python 2.x, super() doesn't know what the current class is. You have
> > to explicitly tell it. If you tell it a lie, surprising things will
> > happen.
>
> > Assuming you accurately tell it the current class, can you give an
> > example where super() doesn't refer to a superclass of the current class?
>
> Brian gave a good example, so I refer you to that.
>
>
>
> >> On a tangent, is it just me, or is the super() documentation incorrect,
> >> or at least unclear?  Quoting from the first two paragraphs:
>
> > Yes, it's a bit unclear, because it's a complex function for dealing with
> > a complicated situation. But if you have single inheritance, it's simple.
> > Anywhere you would write
>
> > class C(B):
> >    def method(self, *args):
> >        B.method(*args)
>
> > you can write
>
> > class C(B):
> >    def method(self, *args):
> >        super(C, self).method(*args)
>
> > and it will Just Work.
>
> Until somebody else comes along and creates class E that inherits from
> both C and D and also uses super(), and now suddenly the super() call
> in C becomes equivalent to "D.method(*args)" instead for instances of
> E, potentially with unexpected results.  Or worse, E.method
> incorrectly calls both C.method and D.method explicitly, and now
> D.method gets invoked twice, once implicitly from C.method, and once
> explicitly from E.method.
>
> I realize that it is the responsibility of the person writing class E
> to make sure they're working with class C correctly, but in order for
> them to do that, ultimately every method in class C should be
> documented as to whether it calls super() or not, and if so whether it
> is designed only for single inheritance or with the MRO in mind.  That
> really shouldn't be necessary,

I was with you up to here.

It's *manifestly* necessary.  One cannot inherit from a class reliably
without knowing how it's implemented.  Knowing the interface is not
enough.  This is not limited to multiple inheritance; it happens with
single inheritance as well, just less often.  It's not limited to
Python, this is true of Java, C++, and every other OOP language I know
of.

Take list.  Say you want a singly inherit from list to implement some
kind of observer that emits a signal every time an item is set.  How
do you do it?  You can't tell by looking at the interface.  Do you
only have to override __setattr__?  Or do you also have to override
other methods (insert, append, etc)?  That depends on whether insert
et al. call __setattr__ or operate at a lower level.  You can't tell
simply by looking at the interface.  You have to have knowledge of the
internals of list.

It's the same problem with multiple inheritance, only exacerbated.
The issues with super is a new dimension, but it's the same problem:
you have to know what you're inheriting from.

This is a fundamental issue with all inheritance.

The problem, in my mind, is that people use inheritance for stuff that
doesn't need it.

class MyFoo(SomebodyElsesFoo):
    def my_tacked_on_method(self):
        self.call_this_method()
        self.then_call_that_method()

Unnecessary and risky.  Do this:

my_tacked_on_function(foo):
    foo.call_this_method()
    foo.then_call_that_method()

The only reason to inherit in a situation like this is if you need
polymorphism (you want to pass your MyFoo to a function that operates
on objects that define a my_tacked_on_method).

A lot Michele Simionato's objections to inheritance are based on the
idea that you don't necessarily know what you're inheriting from.  I
say, if you don't know what you're inheriting from, you shouldn't be
inheriting.  I say this as a strong proponent of inheritance and
multiple inheritance in particular.  I believe it's the best way in
general for objects of different types to share large portions of
their behavior.  But it has its place.

When you have a class you that don't anything about the implementation
of, that is NOT the place for inheritance.  In that case you should
either use composition, or learn what it is you're inheriting from and
take responsibility if the owner changes it.  Or just don't try to
share behavior.

My view of inheritance is a lot like the orthodox Catholic view of
sex.  To Catholics, sex between married people is a wonderful, holy
thing.  To me, inheriting from classes you know the implementation of
is a wonderful, holy thing.  But, sex outside of marriage, and
inheriting from classes you don't know, is deadly sin and you will
burn in Hell.


> and in my current view from a
> maintenance perspective, best practice is to only use super() in the
> multiple-inheritance scenarios it was specifically designed for

I use super() exclusively for all types of inheritance, unless I'm
inheriting from a class I know uses old form (which is none of mine,
and I only rarely inherit from classes I don't own).


Carl Banks



More information about the Python-list mailing list