有没有办法在 CPython 中执行函数创建代码?

Meh*_*dad 3 python hook trace cpython

有什么方法可以挂钩 CPython 解释器,以便每个函数创建 ( def, lambda) 都会调用我定义的过程?sys.settracesys.setprofile不幸的是似乎并没有涵盖deflambda

更新:

似乎 Python 3.7 有f_trace_opcodes......早期版本有任何选择吗?

aba*_*ert 5

opcode在 3.7 之前的版本中没有等效的跟踪。如果有,该功能一开始就不会添加到 3.7 中。

如果您可以升级到 3.7,那么您想要的很简单:

def tracefunc(frame, event, arg):
    if event == 'call':
        frame.f_trace_opcodes = True
    elif event == 'opcode':
        if frame.f_code.co_code[frame.f_lasti] == dis.opmap['MAKE_FUNCTION']:
            makefunctiontracefunc(frame)
    return tracefunc
sys.settrace(tracefunc)
Run Code Online (Sandbox Code Playgroud)

但是如果你不能……你可以做一些更复杂的事情,这取决于你想要这个的原因,但没有一个是远程容易的:

  • 使用line跟踪,并检查代码直到下一行。这对于def来说是微不足道的,但是对于lambda(和理解1)这将是一个很大的痛苦,因为一个lambda(甚至五个)可以出现在语句的中间。您可以ast.parse查看源代码或检查字节码,以找出其中定义了函数,但仍然无法在定义时立即调用您的钩子。
  • 不要使用跟踪,而是编写一个导入挂钩,在导入时修改代码。做到这一点的简单方法可能是在 AST 级别:在解析源之后,使用 a在每个and节点之前或之后NodeTransformer注入对某个函数2 的调用,然后编译转换后的树。但是您也可以在字节码级别使用或,在每个 之前或之后执行此操作。3deflambdabytecodebyteplayMAKE_FUNCTION
  • 脚本pdb而不是编写自己的调试器。我不确定这是否会有所帮助,因为首先pdb无法遍历表达式的一部分。
  • 调试 CPython 本身,并MAKE_FUNCTIONceval调用代码的循环中的处理程序中添加断点。当然,你的代码是在调试器的解释,这可能是对的Pythongdblldb,但它仍然是不一样的Python解释器,你正在调试。而且,虽然可以递归地将代码评估到已调试的解释器中(或触发它的pdb),但这并不容易,而且您在处理它时到处都是段错误。

1.推导式(除了列表推导式,在2.x中)是通过定义然后调用一个函数来实现的。因此,任何依赖于MAKE_FUNCTION操作码或类似方法的方法也会触发推导式,而那些依赖于源代码或 AST 解析的方法则不会(当然,除非您明确这样做)。

2. 显然,您还需要import在每个模块的顶部注入一个以使该功能可用,或者将该功能注入内置模块。

3. 并且MAKE_CLOSURE,对于早期版本的 Python。