Found a shorter way to do super(B, self).test() -> bubble() from below.
(Works with multiple inheritance, doesn't require arguments, correcly behaves with sub-classes)
The solution was to use inspect.getmro(type(back_self)) (where back_self is a self from callee), then iterating it as cls with method_name in cls.__dict__ and verifying that the code reference we have is the one in this class (realized in find_class_by_code_object(self) nested function).
bubble() can be easily extended with *args, **kwargs.
import inspect
def bubble(*args, **kwargs):
def find_class_by_code_object(back_self, method_name, code):
for cls in inspect.getmro(type(back_self)):
if method_name in cls.__dict__:
method_fun = getattr(cls, method_name)
if method_fun.im_func.func_code is code:
return cls
frame = inspect.currentframe().f_back
back_self = frame.f_locals['self']
method_name = frame.f_code.co_name
for _ in xrange(5):
code = frame.f_code
cls = find_class_by_code_object(back_self, method_name, code)
if cls:
super_ = super(cls, back_self)
return getattr(super_, method_name)(*args, **kwargs)
try:
frame = frame.f_back
except:
return
class A(object):
def test(self):
print "A.test()"
class B(A):
def test(self):
# instead of "super(B, self).test()" we can do
bubble()
class C(B):
pass
c = C()
c.test() # works!
b = B()
b.test() # works!
If anyone has a better idea, let's hear it.
Known bug: (thanks doublep) If C.test = B.test --> "infinite" recursion. Although that seems un-realistic for child class to actually have a method, that has been ='ed from parent's one.
Known bug2: (thanks doublep) Decorated methods won't work (probably unfixable, since decorator returns a closure)... Fixed decorator proble with for _ in xrange(5): ... frame = frame.f_back - will handle up to 5 decorators, increase if needed. I love Python!
Performance is 5 times worse than super() call, but we are talking about 200K calls vs a million calls per second, if this isn't in your tightest loops - no reason to worry.