从框架中获取可调用对象
给定一个帧对象(比如通过sys._getframe返回的),我能否获取到底层的可调用对象?
代码说明:
def foo():
frame = sys._getframe()
x = some_magic(frame)
# x is foo, now
请注意,我的问题是如何从一个帧中获取对象,而不是获取当前被调用的对象。
希望这是可能的。
谢谢,
MH
编辑:
我在某种程度上找到了解决这个问题的方法。这主要受到Andreas和Alexander回复的启发。感谢你们的时间投入!
def magic():
fr = sys._getframe(1)
for o in gc.get_objects():
if inspect.isfunction(o) and o.func_code is fr.f_code:
return o
class Foo(object):
def bar(self):
return magic()
x = Foo().bar()
assert x is Foo.bar.im_func
(在2.6.2版本中有效,对于py3k,将func_code
替换为__code__
,将im_func
替换为__func__
)
然后,我可以积极地遍历globals()或gc.get_objects(),并使用dir()查找与给定函数对象对应的可调用对象。
这让我觉得有点不太符合Python的风格,但确实有效。
再次感谢!
MH
3 个回答
这不是一个真正的答案,而是一个评论。我本想把它作为评论发出去,但我的“声望积分”不够。
不过,我觉得这里有一个合理的(我认为)使用场景,说明为什么想要这样做。
我的应用程序使用gtk,并且创建了很多线程。任何同时做这两件事的人都知道,除了主线程之外,你不能直接操作图形界面(GUI)。一个常见的解决办法是把需要操作GUI的函数交给 idle_add()
,这样它就会在主线程中安全地运行。于是,我有很多这样的代码:
def threaded_gui_func(self, arg1, arg2):
if threading.currentThread().name != 'MainThread':
gobject.idle_add(self.threaded_gui_func, arg1, arg2)
return
# code that touches the GUI
如果我能简单地写成这样就好了,这样会更短、更简单(也更方便复制粘贴):
def thread_gui_func(self, arg1, arg2):
if idleIfNotMain(): return
# code that touches the GUI
其中,idleIfNotMain() 如果在主线程中就返回 False,如果不在主线程中,它会使用 inspect(或者其他方法)来找出需要调用的函数和参数,然后交给 idle_add()
,最后返回 True。获取参数我能搞定,但获取函数似乎不是那么简单。 :-(
有点丑,不过就是这样:
frame.f_globals[frame.f_code.co_name]
完整的例子:
#!/usr/bin/env python
import sys
def foo():
frame = sys._getframe()
x = frame.f_globals[frame.f_code.co_name]
print foo is x
foo()
输出'真'。
为了支持所有情况,包括函数是类的一部分还是单纯的全局函数,其实没有简单的方法可以做到这一点。你可能可以获取完整的调用栈,然后通过 globals()
一层层地遍历,但这样做并不优雅...
我能给你提供的最接近的东西是这个:
import sys, types
def magic():
# Get the frame before the current one (i.e. frame of caller)
frame = sys._getframe(1)
# Default values and closure is lost here (because they belong to the
# function object.)
return types.FunctionType(frame.f_code, frame.f_globals)
class MyClass(object):
def foo(self, bar='Hello World!'):
print bar
return magic()
test = MyClass()
new_foo = test.foo()
new_foo(test, 'Good Bye World!')
你将执行完全相同的代码,但它会在一个新的代码包装器中运行(比如说 FunctionType
)。
我猜你是想根据调用栈恢复应用程序的状态... 这里有一些东西,至少能尽可能地像原始调用那样调用函数(闭包的部分仍然没有包含,因为如果你能从栈帧中获取闭包,那么获取被调用的函数就会变得相对简单):
import sys, types
class MyClass(object):
def __init__(self, temp):
self.temp = temp
def foo(self, bar):
print self.temp, bar
return sys._getframe()
def test(hello):
print hello, 'World!'
return sys._getframe()
def recall(frame):
code = frame.f_code
fn = types.FunctionType(
code, frame.f_globals, code.co_name,
# This is one BIG assumption that arguments are always last.
tuple(frame.f_locals.values()[-code.co_argcount:]))
return fn()
test1 = MyClass('test1')
frame1 = test1.foo('Hello World!')
test2 = MyClass('test2')
frame2 = test2.foo('Good Bye World!')
frame3 = test2.foo('Sayonara!')
frame4 = test('HI')
print '-'
recall(frame4)
recall(frame3)
recall(frame2)
recall(frame1)