获取已编译函数对象的功能

16

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函数; 它是否会返回代码对象的注释或者也需要一个函数对象?)

2个回答

8
通过使用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


1
inspect.getframeinfo元组的function字段似乎是一个字符串而不是函数对象。而gc方法会存在多个引用的风险? - Will
我最终使用了gc.get_referrers(...)[0]。 - Will
1
你可以添加一个检查,以查看func_code/__code__是否是您想要的代码对象到列表推导式中。如果您从同一代码对象生成具有不同闭包的多个函数(例如使用装饰器),则仍会失败。我想可以将f_locals与每个func_closure的cell_contents进行比较... - Walter Mundt
关于多个引用者:如果有多个是函数对象的引用者,那么您实际上有多个函数重用相同的代码对象,并且不管如何都必须处理它们。 - jsbueno

1
你可以将函数对象作为模块或类的属性进行检索:
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)

输出: 调用 add {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

这个解决方案的灵感来自于这个问题


有没有保证 self 变量的名称一定是 self 呢?它可能包含完全任意的内容。 - rdb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接