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