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

发布于 2024-11-07 12:35:26 字数 298 浏览 5 评论 0原文

我正在研究Python 2.6.5。

给定一个抽象语法树,我想获取它的子树。

大多数 StackOverflow 帖子讨论 ast.NodeVisitor 及其中定义的方法:visit()generic_visit()。 但是,visit()generic_visit() 不会给出子级,而是直接将函数递归地应用于子级。

有人可以写一段简短的代码来演示它吗? python库中是否存在相同的预定义函数?

I am working on Python 2.6.5.

Given a Abstract Syntax Tree, I want to obtain its children.

Most StackOverflow posts discuss ast.NodeVisitor and the methods defined in it: visit(), generic_visit().
However, visit() and generic_visit() do not give the children, rather they directly apply the function recursively on them.

Can someone please write a short code or so to demonstrate it?
Does there exist a predefined function in python library for the same?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

娇妻 2024-11-14 12:35:26

包含节点子节点的属性取决于节点表示的语法类型。每个节点类还有一个特殊的 _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'>

事实上,如果你看一下语法,第一个产生式规则是针对 Module 的。它似乎采用一系列语句,作为称为主体的参数。

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

AST 的 _fields 属性就是“body”,body 属性是 AST 节点的序列。回到语法,查看 stmt 的产生式规则,我们看到 Expr 采用单个 expr,名为 value

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

如果我们查找对于 BinOp 的定义,我们看到它需要 3 个不同的参数:left、op 和 right。我希望你应该能够从那里继续。

The attaributes containing the node's children depend on the type of syntax the node represents. Every node class also has a special _fields attribute, that lists the attribute names for the child nodes that class has. For instance,

>>> 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>

and so on.

Edit, to clarify what's going on

Before going any further, take a glance at the CPython Abstract Grammar

Consider:

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

In fact, if you look at the grammar, the first production rule is for Module. It appears to take a sequence of statements, as an argument called body.

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

The _fields attribute of the AST is just "body", and the body attribute is a sequence of AST nodes. Back to the grammar, looking in the production rules for stmt, we see that Expr takes a single expr, named value

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

If we look up the definition for BinOp, we see that it takes 3 different arguments, left, op and right. You should be able to proceed from there, I hope.

少女净妖师 2024-11-14 12:35:26

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                                                 

                                                                               `

The ast module provides an iter_child_nodes function you might find useful.

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                                                 

                                                                               `
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文