"inherited" keyword in Python?

Paul Foley see at below
Tue Dec 5 14:28:17 CET 2000


On Tue, 5 Dec 2000 11:11:13 +0100, Alex Martelli wrote:

> "Paul Foley" <see at below> wrote in message
> news:m2elznipbl.fsf at mycroft.actrix.gen.nz...
>     [snip]
>> > Metaprogramming magick was deemed inappropriate here (as I
>> > read the group consensus).  One *might* have a global function
>> > called super(), which tried to gauge the appropriate class
>> > object to use (in single-inheritance cases, only, of course),
>> 
>> Why "of course"?  If you can find out the name of the method which is
>> calling super() (which can be done, but I don't remember how), and the

> Name of calling-function is easy (raise & catch an exception,
> it's in the stackframe you get this way).

That much I knew.  How to get the stack frame was what I didn't know.

> I think your cpl function is much more complicated than what's needed
> to reproduce Python's very simple rule for walking the DAG of base
> classes (it's just depth-first, left-first), even assuming it does
> end up reproducing it perfectly.  But that's not the main point.

It's probably much more complicated than it needs to be to do what it
does, but it's not intended to reproduce Python's rule exactly -- that
rule doesn't allow for a correctly-functioning "super" function in the
presence of multiple inheritance.

My cpl() uses the CLOS rule, which is similar to Python but not
broken.  The entire class hierarchy is sorted in such a way that
classes appearing to the left of other classes in __bases__ appear
before them in the sorted list, every class appears before any of its
bases, and each appears exactly once.  Every method is reachable, not
just the ones up the left branch, as in Python.

> A more interesting theoretical question is how you propose to solve
> the case in which you can't tell which class the current method
> 'belongs' to (also remember a given function-object may be set as
> a method of N classes, for any N, with any set of names -0- but,
> even forgetting that for the moment).  E.g., a very simple case:

If all else fails, take the easy way out and specify it in the call to
super()

> class A:
>     def foo(self):
>         print super()

At first reading, I didn't understand this: I thought it should be an
error, since there are no superclasses.  Reading further, I understand
that you expect super() to return one of the base classes.  Of course,
that won't work.  I thought super() was just supposed to call the
appropriate same-named method of a parent class.  Anything else can be 
done through the usual self.whatever, surely?

> class B(A):
>     def foo(self):
>         print super()
>         A.foo(self)

> class C(A):
>     def foo(self):
>         print super()
>         A.foo(self)

> class D(B,C):
>     def foo(self):
>         print super()
>         B.foo(self)
>         C.foo(self)

In this case, the CPL would be [D B C A], super() in D.foo() would
call B.foo(), whose call to super() would call C.foo(), whose call to
super() would call A.foo(), whose call to super() would signal an error.

> 'super()' in a multiple-inheritance context is a good example.
> There is no single superclass that will do for all attributes
> one might wish to access, so
>     super().foo()

[It was at this point that I realized what your super() was supposed
to be doing...]

> and 'supercall' is always (but always!-) called in the form

>     supercall(self, __thisclass, methodname, *args, **kwds)

> which reduces it to something decently-simple, like:

> def supercall(_obj, _cls, _metnam, *args, **kwds):
>     for _bas in _cls.__bases__:
>         _metobj = getattr(_bas, _metnam, None)
>         if not _metobj is None:
>             return _metobj(_obj, *args, **kwds)
>     else:
>         raise NameError, "No superclass of %s has method %s" % (
>             _cls, _metnam)

> But, how often is it going to be satisfactory to call 'just
> the one' (of several) inherited-method of a certain name, in
> a multiple-inheritance context?  Just as often we may want

It isn't (at least, not to me), which is why I avoided doing it that
way.  Having figured out how to get the frame info, here's an
implementation.  No need to pass in the object and method name:

  import sys

  def super(klass, *args, **kwargs):
      try:
	  1/0
      except:
	  frame = sys.exc_info()[2].tb_frame.f_back
      code = frame.f_code
      name = code.co_name
      self = frame.f_locals[code.co_varnames[0]]
      if args is ():
	  args = map(lambda x, f=frame: f.f_locals[x],
		     code.co_varnames[0:code.co_argcount])
      else:
	  args = [self] + list(args)
      # should do something similar with kwargs here
      classes = cpl(self.__class__)
      for klass in classes[classes.index(klass)+1:]:
	  if klass.__dict__.has_key(name):
	      return apply(klass.__dict__[name], args, kwargs)
      raise AttributeError, "No next method `%s'" % name

If you just call super(__thisclass) with no args other than the class,
it passes on the args the calling method got; or you can specify other
args explicitly [yes, I know this prevents you calling with no args if
super's caller has args]

-- 
The power of accurate observation is commonly called cynicism by those
who have not got it.
                                                    -- George Bernard Shaw
(setq reply-to
  (concatenate 'string "Paul Foley " "<mycroft" '(#\@) "actrix.gen.nz>"))



More information about the Python-list mailing list