如何获取Python函数调用中被调用函数的AST及作用域变量值?
获取被调用函数的AST及作用域变量值的解决方案
我来帮你解决这两个问题——分析函数AST时无法获取被调用函数的AST,以及如何捕获作用域内的变量值,下面是具体的实现思路和代码示例:
一、获取被调用函数的AST
你当前能拿到f2的AST,但无法获取它调用的f1的AST,核心原因是inspect.getsource(f)只能获取目标函数本身的源码,不会自动关联它调用的其他函数。要解决这个问题,需要从f2的Call节点中提取被调用函数的名称,再定位到该函数的定义并解析其AST。
具体实现步骤:
- 遍历目标函数AST,提取被调用函数名
用AST遍历器找到f2代码中的Call节点,提取出被调用函数的标识符(比如f1)。 - 定位被调用函数的对象
根据函数名,从当前模块或对应模块中获取函数对象,可通过globals()、inspect.getmodule()或函数的__module__属性实现。 - 获取被调用函数源码并解析为AST
用inspect.getsource()拿到函数源码,再用ast.parse()解析成AST结构。
代码示例(修改other.py):
import inspect import ast from ast import NodeVisitor class CallVisitor(NodeVisitor): def __init__(self): self.called_funcs = [] def visit_Call(self, node): # 仅处理直接的函数名调用(如f1(),暂不处理obj.f()这类属性调用) if isinstance(node.func, ast.Name): self.called_funcs.append(node.func.id) self.generic_visit(node) def check(f): # 解析目标函数f的AST f_source = inspect.getsource(f) f_ast = ast.parse(f_source) # 遍历f的AST,收集所有被调用的函数名 visitor = CallVisitor() visitor.visit(f_ast) # 获取f所在的模块对象 f_module = inspect.getmodule(f) # 逐个获取被调用函数的AST for func_name in visitor.called_funcs: try: # 从模块中获取函数对象 called_func = getattr(f_module, func_name) # 解析被调用函数的AST called_func_source = inspect.getsource(called_func) called_func_ast = ast.parse(called_func_source) print(f"\n===== 被调用函数 {func_name} 的AST =====") print(ast.dump(called_func_ast, indent=2)) except (AttributeError, TypeError): print(f"无法获取函数 {func_name} 的源码,可能是内置函数或未在当前模块定义") return f_ast
运行主文件后,就能输出f1的完整AST结构了。
二、获取作用域内变量的值
要获取函数作用域内的变量,需要访问函数执行时的栈帧。你提到的装饰器是非常合适的方案——它可以在函数执行前后安全捕获栈帧信息,避免直接执行函数带来的副作用。
方案:用装饰器捕获栈帧变量
编写一个装饰器,在函数执行时记录局部变量和全局变量,后续可传递给check函数使用:
# 主文件代码 from other import check import inspect def track_func_vars(func): def wrapper(*args, **kwargs): # 获取当前栈帧的上一层(即被装饰函数的执行帧) frame = inspect.currentframe().f_back # 复制局部变量和全局变量(避免后续栈帧销毁导致变量丢失) func._local_vars = frame.f_locals.copy() func._global_vars = frame.f_globals.copy() # 打印变量信息(也可存储供check函数使用) print(f"\n===== 函数 {func.__name__} 的作用域变量 =====") print(f"局部变量: {func._local_vars}") print(f"全局变量: {func._global_vars}") return func(*args, **kwargs) return wrapper def f1(): return 1 @track_func_vars def f2(): x = 100 y = x + 50 return f1() # 先执行check分析AST check(f2) # 调用f2触发装饰器捕获变量 f2()
如果希望在check函数中直接访问这些变量,只需在check里读取f._local_vars和f._global_vars即可。
注意事项
- 对于跨模块调用的函数,需要确保被调用模块已被导入,否则
getattr会找不到函数对象; - 如果函数执行有副作用(比如修改文件、发送请求),直接在
check中调用函数捕获变量时要谨慎,建议用装饰器的方式按需触发; - 上述CallVisitor仅处理了简单的函数名调用,若需要支持
obj.method()这类属性调用,需要扩展visit_Call的逻辑,提取属性链中的方法名。
内容的提问来源于stack exchange,提问作者sam33r
相关产品推荐
相关产品推荐

