Use self.super(p, *kw) instead of super(cls, self).func(p, *kw).
Latest version (much faster, with docs, tests, Pyrex version) is available at: http://members.optusnet.com.au/tcdelaney/python.html#autosuper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
import sys class _super (object): """ Wrapper for the super object. If called, a base class method of the same name as the current method will be called. Otherwise, attributes will be accessed. """ __name__ = 'super' # We want the lowest overhead possible - both speed and memory # We need to put the mangled name in as Python 2.2.x didn't work # with private names specified in __slots__. __slots__ = ('_super__super', '_super__method') def __init__(self, super, name): object.__setattr__(self, '_super__super', super) try: object.__setattr__(self, '_super__method', getattr(super, name)) except AttributeError: object.__setattr__(self, '_super__method', name) def __call__(self, *p, **kw): """ Calls the base class method with the passed parameters. """ # We want fastest performance in the normal case - i.e. calling # self.super(*p, **kw). We don't care as much about how long it # takes to fail. method = object.__getattribute__(self, '_super__method') try: return method(*p, **kw) except TypeError: if type(method) is not str: raise # This should throw an AttributeError, but they could have modified # the base class super = object.__getattribute__(self, '_super__super') method = getattr(super, method) object.__setattr__(self, '_super__method', method) return method(*p, **kw) def __getattribute__ (self, name): """ Gets a base class attribute. """ super = object.__getattribute__(self, '_super__super') try: return getattr(super, name) except (TypeError, AttributeError): # Cannot call like self.super.super - produces inconsistent results. if name == 'super': raise TypeError("Cannot get 'super' object of 'super' object") raise def __setattr__(self, name, value): """ All we want to do here is make it look the same as if we called setattr() on a real `super` object. """ super = object.__getattribute__(self, '_super__super') object.__setattr__(super, name, value) def _getSuper (self): """ Gets the `super` object for the class of the currently-executing method. """ frame = sys._getframe().f_back code = frame.f_code name = 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 *might* be the class we're # currently in - however, we need to keep searching until we fail to find # a match. This is due to the way that methods are created - if you have # # class A (autosuper): # def test (self): # pass # # class B (A): # pass # # then calling getattr(B, 'test') will return A.test, with no way to # determine that it's A.test. We only want to use this after calling # getattr(A, 'test') otherwise we will determine the wrong class. cur_class = None for c in type(self).__mro__: try: m = getattr(c, name) func_code = m.func_code except AttributeError: func_code = None if func_code is code: cur_class = c elif cur_class is not None: break if cur_class is None: # We could fail to find the class if we're called from a function # nested in a method raise TypeError, "Can only call 'super' in a bound method" return _super(super(cur_class, self), name) class autosuper (object): """ Automatically determine the correct super object and use it. """ # We want the lowest overhead possible - both speed and memory __slots__ = () super = property(fget=_getSuper, doc=_getSuper.__doc__.strip())
Just call self.super(params) or self.super.<attr> from inside a method and everything works. No name mangling, etc. Now works with Python 2.2 and higher.
This method suffers from a fair bit of overhead - the version on my website does some bytecode hacking to improve performance, at a slight cost in memory usage.
Works with psyco - but strangely, if you import psyco, autosuper slows down a lot (i.e. just the line 'import psyco'). The version on my web site appears to benefit from psyco (for the Python version) although the Pyrex version still has some issues.
The original code used a __metaclass__ - this is not necessary, and in fact a mixin autosuper class is more useful than a metaclass.
Modified to take into account Guido's comments - in particular, AttributeError will now always be raised if no class later in the MRO has that attribute.
Note that it is possible to cause problems if code objects for methods become shared somehow (e.g. the code object for one method is assigned to another). The way psyco replaces code objects (and Raymond Hettinger's constant binding recipe) do not conflict with this recipe.