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_
精彩评论