Python AST:如何获取节点的子节点

6 投票
2 回答
5062 浏览
提问于 2025-04-16 17:40

我正在使用 Python 2.6.5。

给定一个抽象语法树(Abstract Syntax Tree),我想获取它的子节点。

大多数 StackOverflow 上的帖子讨论的是 ast.NodeVisitor 以及里面定义的方法:visit()generic_visit()。但是,visit()generic_visit() 并不会直接返回子节点,而是会对它们递归地应用函数。

有没有人能写一段简短的代码来演示一下?Python 库中有没有现成的函数可以做到这一点?

2 个回答

3

ast模块提供了一个叫做iter_child_nodes的函数,这个函数可能对你很有帮助。

def iter_child_nodes(node):                                                    
    """                                                                        
    Yield all direct child nodes of *node*, that is, all fields that are nodes 
    and all items of fields that are lists of nodes.                           
    """                                                                        
    for name, field in iter_fields(node):                                      
        if isinstance(field, AST):                                             
            yield field                                                        
        elif isinstance(field, list):                                          
            for item in field:                                                 
                if isinstance(item, AST):                                      
                    yield item                                                 

                                                                               `
6

节点的子节点属性取决于该节点代表的语法类型。每个节点类都有一个特别的 _fields 属性,这个属性列出了该类拥有的子节点的属性名称。例如,

>>> ast.parse('5+a')
<_ast.Module object at 0x02C1F730>
>>> ast.parse('5+a').body
[<_ast.Expr object at 0x02C1FF50>]
>>> ast.parse('5+a').body[0]
<_ast.Expr object at 0x02C1FBF0>
>>> ast.parse('5+a').body[0]._fields
('value',)
>>> ast.parse('5+a').body[0].value
<_ast.BinOp object at 0x02C1FF90>
>>> ast.parse('5+a').body[0].value._fields
('left', 'op', 'right')
>>> ast.parse('5+a').body[0].value.left
<_ast.Num object at 0x02C1FB70>

等等。

编辑,澄清一下情况

在继续之前,先看看 CPython 抽象语法

考虑一下:

>>> type(ast.parse('5+a'))
<class '_ast.Module'>

实际上,如果你查看语法,第一条生成规则是针对模块的。它似乎接受一系列语句,作为一个叫做 body 的参数。

>>> ast.parse('5+a')._fields
('body',)
>>> ast.parse('5+a').body
[<_ast.Expr object at 0x02E965B0>]

AST 的 _fields 属性就是“body”,而 body 属性是一系列 AST 节点。回到语法,查看 stmt 的生成规则,我们看到 Expr 接受一个名为 value 的单一表达式。

>>> ast.parse('5+a').body[0].value
<_ast.BinOp object at 0x02E96330>

如果我们查找 BinOp 的定义,会发现它接受三个不同的参数,分别是 left、op 和 right。希望你能从这里继续理解下去。

撰写回答