【问题标题】:Getting the function for a compiled function object获取已编译函数对象的函数
【发布时间】:2026-01-17 06:45:01
【问题描述】:

Python 函数有一个代码对象__code__

sys.settrace 跟踪 frame 有一个 f_code 代码对象。

对于那些作为函数的跟踪器调用,我如何获取函数对象(及其__annotation__ 成员)?

到目前为止,通过反复试验,我有:

if hasattr(frame.f_globals.get(frame.f_code.co_name),"__annotations__"):

这似乎适用于函数,但不适用于类成员函数;更糟糕的是,它会将类成员函数与同名的*函数混淆。

(我在 Python 3.2.3 (Xubuntu) 上。我看到 Python 3.3 inspect 模块有一个 signature 函数;这会返回代码对象的注释还是它也需要函数对象? )

【问题讨论】:

    标签: python python-3.x


    【解决方案1】:

    通过inspect.getframeinfo 模块。 我的意思是——在 Python 中没有直接的方法来做到这一点——大多数时候你可以在没有函数的情况下获取代码对象,它是通过框架自省。

    Inspect 的 getframeinfo 函数确实会返回有关正在运行的帧的一些信息,然后您可以通过获取函数对象的名称来检索函数对象。

    这取决于实现并且有一些缺点:

    >>> import inspect
    >>> def a():
    ...   return inspect.currentframe()
    ... 
    
    >>> inspect.getframeinfo(a())
    Traceback(filename='<stdin>', lineno=2, function='a', code_context=None, index=None)
    >>> b = inspect.getframeinfo(a())
    >>> b.function
    'a'
    

    另一种方法,但仍然依赖于实现,是使用 gc 模块(垃圾收集器)来获取所述代码对象的引用。

    >>> import gc
    >>> from types import FunctionType
    >>> def a(): pass
    ... 
    >>> code = a.__code__
    
    >>> [obj for  obj in  gc.get_referrers(code) if isinstance(obj, FunctionType)  ][0]
    <function a at 0x7f1ef4484500>
    >>> 
    

    -- 这适用于 Python 3 - 对于 Python 2,应将 __code__ 替换为 func_code

    【讨论】:

    • inspect.getframeinfo 元组的function 字段似乎是字符串而不是函数对象。并且 gc 方法有多个引荐来源的风险?
    • 我最终使用了 gc.get_referrers(...)[0]
    • 您可以添加一个检查以查看 func_code/__code__ 是否是您想要列表理解的代码对象。如果您从同一代码对象生成具有不同闭包的多个函数,这仍然会失败,例如与装饰师。我想可以将 f_locals 与 func_closure 的 cell_contents 进行比较...
    • 对于多个referrer:如果有多个referrer都是函数对象,那么实际上你有多个函数复用同一个代码对象,无论如何都要处理。
    【解决方案2】:

    您可以将函数对象作为模块或类的属性来检索:

    import inspect
    import sys
    
    
    def frame_to_func(frame):
        func_name = frame.f_code.co_name
        if "self" in frame.f_locals:
            return getattr(frame.f_locals["self"].__class__, func_name)
        else:
            return getattr(inspect.getmodule(frame), func_name)
    
    
    def tracefunc(frame, event, arg):
        if event in ['call', 'return']:
            func_obj = frame_to_func(frame)
            print(f"{event} {frame.f_code.co_name} {func_obj.__annotations__}")
    
    
    def add(x: int, y: int) -> int:
        return x+y
    
    
    if __name__ == '__main__':
        sys.settrace(tracefunc)
        add(1, 2)
        sys.settrace(None)
    

    输出: call add {'x': &lt;class 'int'&gt;, 'y': &lt;class 'int'&gt;, 'return': &lt;class 'int'&gt;}

    解决方案的灵感来自question

    【讨论】: