如何将当前函数赋值给变量?
我想知道怎么在Python中获取当前正在执行的函数的变量。我不想要函数的名字。我知道可以用inspect.stack
来获取当前函数的名字,但我想要的是实际可以调用的对象。有没有办法不使用inspect.stack
来获取函数名,然后再用eval
来得到可调用的对象呢?
编辑:我这样做是有原因的,不过这个理由并不是特别好。我正在使用plac来解析命令行参数。使用方法是通过plac.call(main)
,这会根据“main”函数的签名生成一个ArgumentParser对象。在“main”函数内部,如果参数有问题,我想要退出并显示一个错误信息,其中包含ArgumentParser对象的帮助文本,这就意味着我需要通过调用plac.parser_from(main).print_help()
来直接访问这个对象。能否改成这样说:plac.parser_from(get_current_function()).print_help()
,这样我就不需要依赖函数名为“main”了。目前,我的“get_current_function”实现是:
import inspect
def get_current_function():
return eval(inspect.stack()[1][3])
不过这个实现依赖于函数有名字,我想这也不是太麻烦。我是不会用plac.call(lambda ...)
的。
从长远来看,或许可以请plac的作者实现一个print_help方法,用来打印最近一次使用plac调用的函数的帮助文本,或者类似的功能。
10 个回答
这是你要的内容,我尽量做到最接近。已经在Python的2.4、2.6和3.0版本中测试过了。
#!/usr/bin/python
def getfunc():
from inspect import currentframe, getframeinfo
caller = currentframe().f_back
func_name = getframeinfo(caller)[2]
caller = caller.f_back
from pprint import pprint
func = caller.f_locals.get(
func_name, caller.f_globals.get(
func_name
)
)
return func
def main():
def inner1():
def inner2():
print("Current function is %s" % getfunc())
print("Current function is %s" % getfunc())
inner2()
print("Current function is %s" % getfunc())
inner1()
#entry point: parse arguments and call main()
if __name__ == "__main__":
main()
输出结果:
Current function is <function main at 0x2aec09fe2ed8>
Current function is <function inner1 at 0x2aec09fe2f50>
Current function is <function inner2 at 0x2aec0a0635f0>
我最近花了很多时间尝试做类似的事情,最后还是放弃了。这其中有很多特殊情况。
如果你只想获取调用栈中最底层的内容,你可以直接用在def
语句中使用的名字。这个名字会通过词法闭包绑定到你想要的函数上。
举个例子:
def recursive(*args, **kwargs):
me = recursive
me
现在会指向你想要的那个函数,无论这个函数是从哪个地方被调用的,只要在定义的地方没有重新定义它。有没有什么原因让这个方法不奏效呢?
至于想要获取在调用栈上层执行的函数,我想不出有什么可靠的方法。
栈帧告诉我们当前正在执行哪个代码对象。如果我们能找到一个函数对象,它的 __code__
属性指向这个代码对象,那我们就找到了这个函数。
幸运的是,我们可以询问垃圾回收器,哪些对象持有对我们代码对象的引用,这样就可以筛选这些对象,而不需要遍历Python世界中每一个活跃的对象。通常,指向一个代码对象的引用数量不多。
需要注意的是,多个函数可以共享同一个代码对象,尤其是在你从一个函数返回另一个函数的情况下,这种情况称为闭包。当有多个函数使用同一个代码对象时,我们就无法确定具体是哪个函数,所以我们返回 None
。
import inspect, gc
def giveupthefunc():
frame = inspect.currentframe(1)
code = frame.f_code
globs = frame.f_globals
functype = type(lambda: 0)
funcs = []
for func in gc.get_referrers(code):
if type(func) is functype:
if getattr(func, "__code__", None) is code:
if funcs:
return None
funcs.append(func)
return funcs[0] if funcs else None
一些测试案例:
def foo():
return giveupthefunc()
zed = lambda: giveupthefunc()
bar, foo = foo, None
print bar()
print zed()
我不太确定这个的性能表现如何,但我觉得应该适合你的使用场景。