Improved super/autosuper

  • Thread starter Delaney, Timothy C (Timothy)
  • Start date
D

Delaney, Timothy C (Timothy)

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/286195

This is a new version of super that automatically determines which
method needs to be called based on the existing stack frames. It's much
nicer for writing cooperative classes. It does have more overhead, but
at this stage I'm not so concerned about that - the important thing is
it actually works.

Note that this uses sys._getframe() magic ...

import sys

_builtin_super = super

def super (self, *p, **kw):
"""
Automatically determine the correct super method and call it.

If there is no corresponding super method, there is no effect
(super just
returns None). This assists in creating cooperative base
classes.

This function is designed to be used with the autosuper
metaclass.

Example of usage:

__metaclass__ = autosuper

class A:

def __init__ (self, a, b):
print 'A.__init__'
print a, b
self.super(a, b)

class B (A):

def __init__ (self, a, b):
print 'B.__init__'
self.super(a, b)

B(1, 2)

produces:

B.__init__
A.__init__
1 2
"""

f = sys._getframe().f_back

# Make sure that we're being called from a bound method
instance = f.f_locals[f.f_code.co_varnames[0]]
assert self is instance

# We'll need this to look up the correct method in the base
classes
fname = f.f_code.co_name

# Find the method we're currently running by scanning the MRO
and comparing
# the code objects - when we find a match, that's the class
whose method
# we're currently executing.
s = None
si = None

for c in type(self).__mro__:
try:
m = getattr(c, fname)
except AttributeError:
continue

if m.im_func.func_code is f.f_code:
s = c
break

# We should *never* fail to find the current class
assert s is not None

# Try to get a base class method. If we don't find one, we're
finished.
try:
m = getattr(_builtin_super(s, self), fname)
except AttributeError:
return None

# If the code object for the super class is the same as the
current code
# object, we've actually picked up the current class again -
which would
# lead to infinite recursion. So we're finished.
try:
if m.func_code is f.f_code:
return None
except AttributeError:
func_code = None

if m is None:
return None

return m(*p, **kw)

class autosuper (type):
def __init__(cls, name, bases, dict):
setattr(cls, 'super', super)

if __name__ == '__main__':

__metaclass__ = autosuper

class A:
def __init__ (self):
print 'A.__init__'
self.super()

def test (self):
print 'A.test'
self.super()

class B (A):
def __init__ (self):
print 'B.__init__'
self.super()

def test (self):
print 'B.test'
self.super()

class C (A):
def __init__ (self):
print 'C.__init__'
self.super()

def test (self):
print 'C.test'
self.super()

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

def test (self):
print 'D.test'
self.super()

A().test()
print
B().test()
print
C().test()
print
D().test()

---------- Run ----------
A.__init__
A.test

B.__init__
A.__init__
B.test
A.test

C.__init__
A.__init__
C.test
A.test

D.__init__
B.__init__
C.__init__
A.__init__
D.test
B.test
C.test
A.test

Output completed (0 sec consumed) - Normal Termination

Tim Delaney
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top