在 Python 中获取 AST 节点的父节点

Getting parent of AST node in Python

我在 Python 3 中使用抽象语法树。ast 库提供了很多获取节点子节点的方法(您可以使用 iter_child_nodes()walk()) 但没有办法获得其中一个的 parent。此外,每个节点都有指向其子节点的链接,但没有指向其父节点的链接。

如果我不想为 ast 库编写一些插件,如何获取 AST 节点的父节点

最正确的方法是什么?

您可以创建一些散列 table 将 AST 节点关联到 AST 节点并扫描(递归地)最顶层的 AST 树以在该散列 table 中注册每个节点的父节点。

它不会是一个真正的插件,但您总是可以编写一个函数,为每个子项的父项添加一个 weakref。

下面是一些实际代码:

for node in ast.walk(root):
    for child in ast.iter_child_nodes(node):
        child.parent = node

不需要散列table,您可以直接在节点上放置一个属性。

你也可以使用ast.NodeTransformer来实现:

代码:

import ast


class Parentage(ast.NodeTransformer):
    parent = None

    def visit(self, node):
        node.parent = self.parent
        self.parent = node
        node = super().visit(node)
        if isinstance(node, ast.AST):
            self.parent = node.parent
        return node

用法:

module = Parentage().visit(ast.parse('def _(): ...'))
assert module.parent is None
assert module.body[0].parent is module

稍后当您想以其他方式编辑树时,您可以子类化:

class SomeRefactoring(Parentage):
    def visit_XXX(node):
        self.generic_visit(node)
        f'do some work on {node.parent} here if you want'
        return node

注:

值得注意的是,一些节点可以有多个父节点。例如:

module = ast.parse("warnings.warn('Dinosaurs!')")
func = module.body[0].value.func
name, ctx = ast.iter_child_nodes(func)
assert ctx is next(ast.iter_child_nodes(name))

这表明同一个 ast.Load 节点 ctx 有两个父节点 - funcname。父节点将由节点在树中出现的最后位置设置。