16

Python 函数有一个代码对象__code__

sys.settrace跟踪具有代码对象framef_code

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

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

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

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

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

4

2 回答 2

7

通过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

于 2012-10-08T18:12:43.913 回答
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)

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

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

于 2020-04-24T09:53:09.063 回答