在 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
有两个父节点 - func
和 name
。父节点将由节点在树中出现的最后位置设置。
我在 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
有两个父节点 - func
和 name
。父节点将由节点在树中出现的最后位置设置。