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