Python 3.7中使用ast库计算抽象语法树深度的实现及结果偏差排查
计算结果偏差的原因
原代码的递归逻辑存在累加错误
你代码中的depth是函数栈帧内的局部变量,在遍历单个AST节点的所有字段时,每遍历一个字段就执行一次depth += 1,导致同一个父节点下的不同子节点,拿到的初始深度并非统一的「父节点深度+1」,而是在前一个子节点遍历完成后的累加值上继续抬升,完全打乱了深度计算的逻辑。
举个实际执行路径的例子:
- 当遍历到
Call节点时,进入对应栈帧的初始depth为3,该节点共有3个字段(func、args、keywords)- 遍历第一个字段
func时,depth从3累加到4,传入子节点递归计算,返回结果7。此时栈帧内的depth已变为4 - 遍历第二个字段
args时,depth从4累加到5,传入参数列表的递归计算。此时栈帧内的depth变为5 - 遍历第三个字段
keywords时,depth从5累加到6,传入空关键字列表的递归计算。此时栈帧内的depth变为6
- 遍历第一个字段
这种遍历方式下,最终计算得到的8层结果,本质是同层字段不断累加depth带来的虚高值,不符合AST深度的定义。
原预估节点链路的结构验证
修正上述累加逻辑后,实际计算结果和你预估的6层深度是匹配的。你之前列出的链路是Module -> Expr -> Call -> Attribute -> Name -> 'pd',覆盖了函数名取值的核心路径,其余分支的最大深度和这条路径一致:
Module节点(根,深度0)Module的_body列表(深度1)- 列表内的
Expr表达式节点(深度2) Expr的value字段对应的Call调用节点(深度3)Call的func字段对应的Attribute属性节点(深度4)Attribute的value字段对应的Name('pd')变量节点(深度5)Name的id字段对应的字符串'pd'(深度6)
其余分支比如参数elevations对应的节点链路,最大深度同样为6,不存在额外的深度抬升。
修正后的深度计算实现
调整递归逻辑,每个子节点的深度独立基于当前节点深度+1计算,不修改栈帧内的原始depth值,参考实现如下:
import ast def depth_ast(tree, depth=0): # 遇到原生类型(字符串、数字、None等无子结构的值),直接返回当前深度 if not isinstance(tree, (ast.AST, list)): return depth branches_depth = [] if isinstance(tree, ast.AST): # 遍历每个字段,为子节点传入独立计算的depth+1值 for field in tree._fields: child = getattr(tree, field) branches_depth.append(depth_ast(child, depth + 1)) elif isinstance(tree, list): # 遍历列表每个元素,为元素传入独立计算的depth+1值 for item in tree: branches_depth.append(depth_ast(item, depth + 1)) # 没有可遍历子结构时(空列表、无字段的节点如Load()),返回当前深度 return max(branches_depth) if branches_depth else depth py_ast = ast.parse('pd.read_json(elevations)') print(depth_ast(py_ast)) # 输出符合预期的6
内容的提问来源于stack exchange,提问作者NathanaëlBeau
相关产品推荐
相关产品推荐

