Trying to convert into a simple nice super(B, self).method()bubble() call.
Did it, see below!
Is it possible to get reference to class B in this example?
class A(object): pass
class B(A):
def test(self):
test2()
class C(B): pass
import inspect
def test2():
frame = inspect.currentframe().f_back
cls = frame.[?something here?]
# cls here 开发者_如何学编程should == B (class)
c = C()
c.test()
Basically, C is child of B, B is child of A. Then we create c of type C. Then the call to c.test() actually calls B.test() (via inheritance), which calls to test2().
test2() can get the parent frame frame; code reference to method via frame.f_code;
self via frame.f_locals['self']; but type(frame.f_locals['self']) is C (of course), but not B, where method is defined.
Any way to get B?
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.
Although this code should never be used for any normal purpose. For the sake of answering the question, here's something working ;)
import inspect
def test2():
funcname = inspect.stack()[1][3]
frame = inspect.currentframe().f_back
self = frame.f_locals['self']
return contains(self.__class__, funcname)
def contains(class_, funcname):
if funcname in class_.__dict__:
return class_
for class_ in class_.__bases__:
class_ = contains(class_, funcname)
if class_:
return class_
加载中,请稍侯......
精彩评论