Python AST: как получить дочерние элементы узла

#python #abstract-syntax-tree

#python #абстрактное синтаксическое дерево

Вопрос:

Я работаю над Python 2.6.5.

Учитывая абстрактное синтаксическое дерево, я хочу получить его дочерние элементы.

В большинстве сообщений StackOverflow обсуждаются ast.NodeVisitor и определенные в нем методы: visit() , generic_visit() . Однако, visit() и generic_visit() не предоставляют дочерних элементов, скорее они непосредственно рекурсивно применяют функцию к ним.

Может кто-нибудь, пожалуйста, написать короткий код или около того, чтобы продемонстрировать это? Существует ли предопределенная функция в библиотеке python для того же самого?

Ответ №1:

Атрибуты, содержащие дочерние элементы узла, зависят от типа синтаксиса, который представляет узел. У каждого класса узла также есть специальный _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 . Похоже, что он принимает последовательность операторов в качестве аргумента, называемого body.

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

_fields Атрибутом AST является просто «body», а атрибутом body является последовательность узлов AST. Возвращаясь к грамматике, просматривая производственные правила для stmt , мы видим, что Expr для этого требуется единственное выражение с именем value

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

Если мы посмотрим определение для BinOp, мы увидим, что оно принимает 3 разных аргумента: left, op и right. Я надеюсь, вы сможете продолжить оттуда.

Комментарии:

1. спасибо за ваш ответ! Однако я еще не понял это должным образом… итак, node._fields предоставит дочерние элементы этого конкретного узла? (в виде кортежа?) …. тогда что означают тело и значение? спасибо за вашу помощь!

Ответ №2:

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                                                 

                                                                               `