using super

Scott David Daniels Scott.Daniels at Acm.Org
Tue Jan 1 06:11:48 CET 2008


Steven D'Aprano wrote:
> On Mon, 31 Dec 2007 16:19:11 -0800, Scott David Daniels wrote:
> 
>> Steven D'Aprano wrote:
>>> On Mon, 31 Dec 2007 08:03:22 -0800, Scott David Daniels wrote:
>>>> Steven D'Aprano wrote: ...
>>>>> def chain(meth):  # A decorator for calling super.
>>>>>     def f(self, *args, **kwargs):
>>>>>         result = meth(self, *args, **kwargs)
>>>>>         S = super(self.__class__, self)
>>>> This line is the problem.  The class parameter needs to be the class
>>>> (B in this case) in which the chaining method is defined, not that of
>>>> the object itself.
>>> One minor correction: the class parameter needs to be the class
>>> *itself*, not the class *name* (which would be the string "B").
>> Point taken.
>>
>>> I don't quite understand your description though. What do you mean "the
>>> chaining method is defined"? chain() is defined outside of a class.
>> The class where f (the chaining method) is defined; equivalently, the
>> class in which the @chain is used.
> 
> So why doesn't self.__class__ work? That's the class in which @chain is 
> used.

OK, here's a simple 3-class example:

     class A(object):
         def meth(self): print 'A.meth:', self.__class__, '---'
         def pn(self): return '<A>'

     class B(A):
         def meth(self):
             super(B, self).meth()
             print 'B.meth:', self.__class__, super(B, self).pn()
         def pn(self): return '<B>'

     class C(B):
         def meth(self):
             super(C, self).meth()
             print 'C.meth:', self.__class__, super(C, self).pn()
         def pn(self): return '<C>'

     c = C()
     c.meth()
     # Figure out why it printed what it did.

     # If not clear yet, how about this:
     for class_ in C, B:
         print class_.__name__, super(class_, c).pn()

     # And a bigger example (re-using A) to show why we
     class B0(A):
         def meth(self):
             super(B0, self).meth()
             print 'B0.meth:', self.__class__, super(B0, self).pn()
         def pn(self): return '<B0>'

     class B1(B0):
         def meth(self):
             super(B1, self).meth()
             print 'B1.meth:', self.__class__, super(B1, self).pn()
         def pn(self): return '<B1>'

     class B2(B0):
         def meth(self):
             super(B2, self).meth()
             print 'B2.meth:', self.__class__, super(B2, self).pn()
         def pn(self): return '<B2>'

     class C1(B1, B2):
         def meth(self):
             super(C1, self).meth()
             print 'C1.meth:', self.__class__, super(C1, self).pn()
         def pn(self): return '<C1>'

     class D1(C1):
         def meth(self):
             super(D1, self).meth()
             print 'D1.meth:', self.__class__, super(D1, self).pn()
         def pn(self): return '<D1>'

     d = D1()
     d.meth()
     # Figure out why it printed what it did.

     for class_ in D1, C1, B1, B2, B0:
         print class_.__name__, super(class_, d).pn()
     # Now (after much cogitation) might that do it?

     # finally, just a fillip, predict this before you run it:
     class E(D1, C):
         def meth(self):
             super(E, self).meth()
             print 'E.meth:', self.__class__, super(E, self).pn()
         def pn(self): return '<E>'

     e = E()
     e.meth()
     for class_ in E, D1, C1, B1, B2, B0, C, B:
         print class_.__name__, super(class_, e).pn()


> I can clearly see that it doesn't work, I just don't understand why. I'd 
> be inclined to chalk it up to super() being a mysterious black box that 
> makes no sense *wink* ....

super (and mro) work to get to all the superclasses in an order that 
produces subtypes before their supertypes.  The diamond inheritance 
examples "show" why its needed.

-Scott



More information about the Python-list mailing list