如何将当前函数赋值给变量?

37 投票
10 回答
35266 浏览
提问于 2025-04-16 08:48

我想知道怎么在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 个回答

16

这是你要的内容,我尽量做到最接近。已经在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>
16

我最近花了很多时间尝试做类似的事情,最后还是放弃了。这其中有很多特殊情况。

如果你只想获取调用栈中最底层的内容,你可以直接用在def语句中使用的名字。这个名字会通过词法闭包绑定到你想要的函数上。

举个例子:

def recursive(*args, **kwargs):
    me = recursive

me 现在会指向你想要的那个函数,无论这个函数是从哪个地方被调用的,只要在定义的地方没有重新定义它。有没有什么原因让这个方法不奏效呢?

至于想要获取在调用栈上层执行的函数,我想不出有什么可靠的方法。

36

栈帧告诉我们当前正在执行哪个代码对象。如果我们能找到一个函数对象,它的 __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()

我不太确定这个的性能表现如何,但我觉得应该适合你的使用场景。

撰写回答