在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)
但是,如果您不能…有很多更复杂的事情可以做,这取决于您想要这样做的原因,但是它们都不是一件容易的事:
>使用行跟踪,并检查代码,直到下一行.对于def来说,这是微不足道的,但是对于lambda(和理解力1)来说,这将是一个很大的痛苦,因为lambda(甚至其中的五个)可能会出现在语句的中间.您可以ast.parse源,或检查字节码,以发现其中定义了函数,但是在定义时仍然无法正确调用钩子.
>而不是使用跟踪,而是编写一个导入钩子,在导入代码时对其进行修改.最简单的方法可能是在AST级别:解析源之后,使用NodeTransformer在每个def和lambda节点之前或之后注入对某个function2的调用,然后编译转换后的树.但是您也可以在每个MAKE_FUNCTION.3之前或之后在字节码级别使用bytecode或byteplay进行操作.
>编写pdb脚本,而不是编写自己的调试器.我不确定这是否会有所帮助,因为pdb首先没有办法遍历表达式的一部分.
>调试CPython本身,并在调用代码的ceval循环的MAKE_FUNCTION处理程序中添加断点.当然,您的代码在调试器的解释器中-可以是适用于gdb和lldb的Python,但仍不是您要调试的Python解释器.而且,虽然可以将代码递归地评估到调试的解释器中(或触发其pdb),但这并不容易,而且在执行过程中您到处都是段错误.
1.理解(2.x中的列表理解除外)是通过定义然后调用一个函数来实现的.因此,任何依赖MAKE_FUNCTION操作码或类似方法的方法也将在理解时触发,而那些依赖源或AST解析的方法则不会(当然,除非您明确地这样做).
2.显然,您还需要在每个模块的顶部插入一个导入,以使该功能可用,或将该功能插入内置模块.
3.还有MAKE_CLOSURE,用于早期版本的Python.