Python如何获取调用函数(而不仅仅是它的名字)?

时间:2016-08-22 11:33:54

标签: python function python-3.x inspect

我想编写一个返回调用函数的函数:

def foo():
    return get_calling_function() #should return 'foo' function object

网上有很多例子如何获取调用函数的名称,而不是如何获取实际对象。我想出了以下获得名称的解决方案,然后在调用函数的全局命名空间中查找它。但是这对于类函数不起作用,因为你也需要类名,而且我想象还有一堆其他的边缘情况。

from inspect import stack
def get_calling_function():
    return stack()[2][0].f_globals[stack()[1][3]]

那么任何建议如何或者是否可以编写这个函数以便它一般地工作(在Python 3,顺便说一句)?谢谢。

1 个答案:

答案 0 :(得分:4)

可以从任何代码对象(以及扩展模块/内置)进行调用:来自execexecfile,来自模块名称空间(导入期间),来自类定义,来自内部方法/ classmethod / staticmethod,来自装饰的函数/方法,来自嵌套函数,... - 所以没有"调用函数"总的来说,很难做到这一点。

堆栈框架及其代码对象是最常用的 - 并检查属性。

这个在许多情况下找到调用函数:

import sys, inspect

def get_calling_function():
    """finds the calling function in many decent cases."""
    fr = sys._getframe(1)   # inspect.stack()[1][0]
    co = fr.f_code
    for get in (
        lambda:fr.f_globals[co.co_name],
        lambda:getattr(fr.f_locals['self'], co.co_name),
        lambda:getattr(fr.f_locals['cls'], co.co_name),
        lambda:fr.f_back.f_locals[co.co_name], # nested
        lambda:fr.f_back.f_locals['func'],  # decorators
        lambda:fr.f_back.f_locals['meth'],
        lambda:fr.f_back.f_locals['f'],
        ):
        try:
            func = get()
        except (KeyError, AttributeError):
            pass
        else:
            if func.__code__ == co:
                return func
    raise AttributeError("func not found")

# Usage

def f():
    def nested_func():
        print get_calling_function()
    print get_calling_function()
    nested_func()

class Y:
    def meth(self, a, b=10, c=11):
        print get_calling_function()
        class Z:
            def methz(self):
                print get_calling_function()
        z = Z()
        z.methz()
        return z
    @classmethod
    def clsmeth(cls):
        print get_calling_function()
    @staticmethod
    def staticmeth():
        print get_calling_function()

f()
y = Y()
z = y.meth(7)
z.methz()
y.clsmeth()
##y.staticmeth()  # would fail

它找到了:

<function f at 0x012E5670>
<function nested_func at 0x012E51F0>
<bound method Y.meth of <__main__.Y instance at 0x01E41580>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method classobj.clsmeth of <class __main__.Y at 0x01F3CF10>>