Why does super() require the class as the first argument?

Steve Holden steve at holdenweb.com
Thu Feb 3 15:45:42 EST 2005


Kevin Smith wrote:

> I like the idea of the super() function, but it doesn't seem to solve 
> the problem that I'm trying to fix.  I don't like hard-coding in calls 
> to super classes using their names:
> 
> class A(object):
>     def go(self):
>         ...
> 
> class B(A):
>     def go(self):
>         ...
>         A.go(self)
> 
> I don't like this because if I ever change the name of 'A', I have to go 
> through all of the methods and change the names there too.  super() has 
> the same problem, but I'm not sure why.  It seems like I should be able 
> to do:
> 
> class B(A):
>     def go(self):
>         ...
>         super(self).go()
> 
> I can create a super() that does this as follows:
> 
> _super = super
> def super(obj, cls=None):
>     if cls is None:
>         return _super(type(obj), obj)
>     return super(cls, obj)
> 
> I guess I'm just not sure why it wasn't done that way in the first place.
> 
You perhaps already know that super() searches the method resolution 
order (list of superclasses) to find a given class's superclass. 
However, in cases involving multiple inheritance (and specifically when 
subclassing two classes that have common ancestors) it's necessary to 
"flatten out" the inheritance tree.

The reason that super() takes a class as argument is because the 
superclass depends *not* on the class in which the method is defined, 
but on *the class of the instance calling the method*.

If we take the example from pp 89-90 of Python in a Nutshell you might 
understand. Consider these definitions:

class A(object):
   def met(self):
     print 'A.met'

class B(A):
   def met(self):
     print 'B.met'
     A.met(self)

class C(A):
   def met(self):
     print 'C.met'
     A.met(self)

class D(B,C):
   def met(self):
     print 'D.met'
     B.met(self)
     C.met(self)

d = D()
d.met()

Unfortunately this prints out

D.met
B.met
A.met
C.met
A.met

In other words, A.met is called by both its subclasses, which is usually 
not quite what you want. If we change the code to

class A(object): # mro: A, object
   def met(self):
     print 'A.met'

class B(A): # mro: B, A, object
   def met(self):
     print 'B.met'
     super(B, self).met()

class C(A): # mro: C, A, object
   def met(self):
     print 'C.met'
     super(C, self).met()

class D(B,C): # mro: D, B, C, A, object
   def met(self):
     print 'D.met'
     super(D, self).met()

d = D()
d.met()

This does indeed print

D.met
B.met
C.met
A.met

Why? The superobject (the object returned by super) has an mro which 
begins AFTER the first argument to super. This has the happy effect of 
changing the mro according to the class of self - super(X, self) is 
superobject, effectively an instance of a class distinguishable from 
self.__class__ only by  the fact that its mro is shorter, losing 
everything up to and including X.

Does this help at all? This is quite a subtle point, but it will repay 
study with an understanding of exactly how you can linearize your 
diamond-shaped inheritance graphs. Very pythonic ...

regards
  Steve
-- 
Meet the Python developers and your c.l.py favorites March 23-25
Come to PyCon DC 2005                      http://www.pycon.org/
Steve Holden                           http://www.holdenweb.com/



More information about the Python-list mailing list