如何在每条Python语句执行后自动调用指定函数?
实现Python语句后自动执行指定函数的方法
可以实现,以下是几种无需手动逐行添加调用的实用方案:
1. 使用sys.settrace()跟踪代码执行
Python的sys.settrace()能设置跟踪函数,在代码执行的不同阶段触发回调。我们可以利用它在每条语句执行完成后调用目标函数。
示例代码:
import sys def after_statement(): print("这条语句执行完了!") def trace_func(frame, event, arg): # 只响应语句执行完成的事件 if event == 'line': after_statement() return trace_func # 启动全局跟踪 sys.settrace(trace_func) # 测试代码 a = 1 + 2 print(a) b = "hello"
注意:该方法会对全局代码生效(包括标准库语句),存在一定性能开销,更适合调试或小范围场景使用。
2. 用AST修改代码自动插入调用
通过Python的ast模块解析代码的抽象语法树,在每个语句节点后插入调用目标函数的节点,再编译执行修改后的AST。这种方式更精准,不会干扰无关代码。
示例代码:
import ast import inspect def after_statement(): print("语句执行完成") class InsertCallVisitor(ast.NodeTransformer): def visit_Expr(self, node): # 在每个表达式语句后插入调用节点 call_node = ast.Call( func=ast.Name(id='after_statement', ctx=ast.Load()), args=[], keywords=[] ) return ast.Block([node, ast.Expr(value=call_node)]) def visit_Assign(self, node): # 在赋值语句后插入调用节点 call_node = ast.Call( func=ast.Name(id='after_statement', ctx=ast.Load()), args=[], keywords=[] ) return ast.Block([node, ast.Expr(value=call_node)]) def auto_call_after_statements(func): # 获取函数源代码并解析为AST source = inspect.getsource(func) tree = ast.parse(source) # 遍历修改AST visitor = InsertCallVisitor() modified_tree = visitor.visit(tree) ast.fix_missing_locations(modified_tree) # 编译执行修改后的代码 exec(compile(modified_tree, filename='<ast>', mode='exec'), globals(), locals()) return locals()[func.__name__] # 测试用函数 @auto_call_after_statements def test_func(): x = 10 y = x * 2 print(y) test_func()
这种方法适合针对特定函数内的语句处理,不会影响全局代码,性能损耗更小。
3. 字节码层面注入调用(进阶)
通过修改函数的代码对象,在字节码层面插入调用目标函数的指令。这种方式较为底层,需要了解Python字节码知识,适合对性能要求较高的场景。
示例代码(简化版):
import types import dis def after_statement(): print("语句执行完了") def inject_call(func): original_code = func.__code__ # 生成调用after_statement的字节码指令 # LOAD_GLOBAL after_statement -> CALL_FUNCTION 0 -> POP_TOP call_bytes = b'\x64\x00\x00\x83\x00\x00\x01' # 合并原字节码与新指令(实际需按语句拆分插入,此处为简化示例) new_code = types.CodeType( original_code.co_argcount, original_code.co_posonlyargcount, original_code.co_kwonlyargcount, original_code.co_nlocals, original_code.co_stacksize + 2, original_code.co_flags, original_code.co_code + call_bytes, original_code.co_consts, original_code.co_names + ('after_statement',), original_code.co_varnames, original_code.co_filename, original_code.co_name, original_code.co_firstlineno, original_code.co_lnotab, original_code.co_freevars, original_code.co_cellvars ) func.__code__ = new_code return func # 测试 @inject_call def test(): a = 5 print(a) test()
注意:字节码指令依赖Python版本,不同版本可能存在差异,维护成本较高。
内容的提问来源于stack exchange,提问作者T. F.
相关产品推荐
相关产品推荐

