[Python-Dev] super()

Guido van Rossum guido@python.org
Fri, 24 Aug 2001 13:09:37 -0400

Thomas Heller wrote (in private email, responding to a checkin message):

> From: "Guido van Rossum" <gvanrossum@users.sourceforge.net>
> > Add 'super', another new object type with magical properties.
> > 
> > super(type) -> unbound super object
> > super(type, obj) -> bound super object; requires isinstance(obj, type)
> > 
> > Typical use to call a cooperative superclass method:
> > 
> > class C(B):
> >     def meth(self, arg):
> >         super(C, self).meth(arg);
> Shouldn't that be
> class C(B):
>     def meth(self, arg):
>         super(B, self).meth(arg)
>               ^
> to call B.meth(self, arg)?
> Thomas

No.  Good question, though!  You have to pass your *own* class so that
this finds the right super-method when multiple inheritance involving
a "diamond" diagram.  You may want to read the section on Method
resolution order in PEP 253 first.

Look at this example:

    class A(object):
	def meth(self, a):
	    return "A.meth(%r)" % a

    print A().meth(1)

    class B(A):
	def __init__(self):
	    self.__super = super(B, self)
	def meth(self, a):
	    return "B.meth(%r) -> " % a + self.__super.meth(a)

    print B().meth(2)

    class C(A):
	__dynamic__ = 1
	def meth(self, a):
	    return "C.meth(%r) -> " % a + self.__super.meth(a)
    C._C__super = super(C)

    print C().meth(3)

    class D(C, B):
	def meth(self, a):
	    return "D.meth(%r) -> " % a + super(D, self).meth(a)

    print D().meth(4) # D.meth(4) -> C.meth(4) -> B.meth(4) -> A.meth(4)

D has the following inheritance graph:

                  ^ ^
                 /   \
                /     \
               /       \
              /         \
              C          B
              ^         ^
               \       /
                \     /
                 \   /
                  \ /

When you have a C or B instance, C.meth's super references A.
But when you have a D instance, C.meth's super references B!

In other words, D.meth calls C.meth calls B.meth calls A.meth.  This
is why you need to pass self to super() -- it needs the __mro__
sequence of the instance.  Remember that D.__mro__ == (D,C,B,A,object).
C.meth calls super(C, D()).meth.  This looks for C in D.__mro__, and
then starts searching for meth at the next class -- finding B.meth.

If you wonder *why* the MRO is designed this way, imagine that meth()
does some sort of saving to disk.  Each subclass saves its own stuff
and then calls the save method of its base class.  Without the
behavior described above, D.save() would have to call C.save() and
B.save(), but this would call A.save() twice!

This is part of a pattern called cooperative class design, described
in the book Putting Metaclasses to Work (for a reference, see PEP

--Guido van Rossum (home page: http://www.python.org/~guido/)