Содержание страницы
ast – абстрактные синтаксические деревья¶ast – Abstract Syntax Trees
Исходный код: Lib/ast.py
Модуль ast помогает приложениям Python обрабатывать деревья абстрактного синтаксиса языка Python. Абстрактный синтаксис может меняться от версии к версии; с помощью этого модуля можно программно узнать, как выглядит текущая грамматика.
Абстрактное синтаксическое дерево можно создать, передав ast.PyCF_ONLY_AST в качестве флага встроенной функции compile() или используя вспомогательную функцию parse() из этого модуля. Результатом будет дерево объектов, классы которых наследуют от ast.AST. Абстрактное синтаксическое дерево можно скомпилировать в объект кода Python с помощью встроенной функции compile().
Классы узлов¶Node classes
-
class
ast.AST¶ Это базовый класс всех узлов AST. Конкретные классы узлов порождаются из файла
Parser/Python.asdl, который воспроизведён ниже. Они определены в C-модуле_astи реэкспортированы вast.Для каждого символа левой части абстрактной грамматики определён один класс (например,
ast.stmtилиast.expr). Кроме того, для каждого конструктора правой части определён один класс; эти классы наследуют от классов для деревьев левой части. Например,ast.BinOpнаследует отast.expr. Для продукционных правил с альтернативами (так называемых «сумм») класс левой части является абстрактным: создаются только экземпляры конкретных узлов-конструкторов.-
_fields¶ Каждый конкретный класс имеет атрибут
_fields, который содержит имена всех дочерних узлов.Каждый экземпляр конкретного класса имеет по одному атрибуту для каждого дочернего узла, тип которого определён в грамматике. Например, экземпляры
ast.BinOpимеют атрибутleftтипаast.expr.Если эти атрибуты помечены как необязательные в грамматике (с помощью вопросительного знака), значением может быть
None. Если атрибуты могут иметь ноль или более значений (помечены звёздочкой), значения представляются в виде списков Python. Все возможные атрибуты должны присутствовать и иметь корректные значения при компиляции AST с помощьюcompile().
-
lineno¶ -
col_offset¶ Экземпляры подклассов
ast.exprиast.stmtимеютlinenoиcol_offsetатрибуты.lineno– это номер строки исходного текста (нумерация с 1, то есть первая строка – строка 1), аcol_offset– смещение в байтах UTF-8 первого токена, который породил узел. Смещение в UTF-8 записывается, потому что синтаксический анализатор использует UTF-8 внутри.
Конструктор класса
ast.Tразбирает свои аргументы следующим образом:Если есть позиционные аргументы, их должно быть столько же, сколько элементов в
T._fields; они будут присвоены как атрибуты с этими именами.Если есть ключевые аргументы, они установят атрибуты с теми же именами в указанные значения.
Например, чтобы создать и заполнить узел
ast.UnaryOp, можно использоватьnode = ast.UnaryOp() node.op = ast.USub() node.operand = ast.Num() node.operand.n = 5 node.operand.lineno = 0 node.operand.col_offset = 0 node.lineno = 0 node.col_offset = 0
или более компактный
node = ast.UnaryOp(ast.USub(), ast.Num(5, lineno=0, col_offset=0), lineno=0, col_offset=0)
-
Абстрактная грамматика¶Abstract Grammar
Абстрактная грамматика в настоящее время определяется следующим образом:
-- ASDL's 7 builtin types are:
-- identifier, int, string, bytes, object, singleton, constant
--
-- singleton: None, True or False
-- constant can be None, whereas None means "no value" for object.
module Python
{
mod = Module(stmt* body)
| Interactive(stmt* body)
| Expression(expr body)
-- not really an actual node but useful in Jython's typesystem.
| Suite(stmt* body)
stmt = FunctionDef(identifier name, arguments args,
stmt* body, expr* decorator_list, expr? returns)
| AsyncFunctionDef(identifier name, arguments args,
stmt* body, expr* decorator_list, expr? returns)
| ClassDef(identifier name,
expr* bases,
keyword* keywords,
stmt* body,
expr* decorator_list)
| Return(expr? value)
| Delete(expr* targets)
| Assign(expr* targets, expr value)
| AugAssign(expr target, operator op, expr value)
-- 'simple' indicates that we annotate simple name without parens
| AnnAssign(expr target, expr annotation, expr? value, int simple)
-- use 'orelse' because else is a keyword in target languages
| For(expr target, expr iter, stmt* body, stmt* orelse)
| AsyncFor(expr target, expr iter, stmt* body, stmt* orelse)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(withitem* items, stmt* body)
| AsyncWith(withitem* items, stmt* body)
| Raise(expr? exc, expr? cause)
| Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
| Assert(expr test, expr? msg)
| Import(alias* names)
| ImportFrom(identifier? module, alias* names, int? level)
| Global(identifier* names)
| Nonlocal(identifier* names)
| Expr(expr value)
| Pass | Break | Continue
-- XXX Jython will be different
-- col_offset is the byte offset in the utf8 string the parser uses
attributes (int lineno, int col_offset)
-- BoolOp() can use left & right?
expr = BoolOp(boolop op, expr* values)
| BinOp(expr left, operator op, expr right)
| UnaryOp(unaryop op, expr operand)
| Lambda(arguments args, expr body)
| IfExp(expr test, expr body, expr orelse)
| Dict(expr* keys, expr* values)
| Set(expr* elts)
| ListComp(expr elt, comprehension* generators)
| SetComp(expr elt, comprehension* generators)
| DictComp(expr key, expr value, comprehension* generators)
| GeneratorExp(expr elt, comprehension* generators)
-- the grammar constrains where yield expressions can occur
| Await(expr value)
| Yield(expr? value)
| YieldFrom(expr value)
-- need sequences for compare to distinguish between
-- x < 4 < 3 and (x < 4) < 3
| Compare(expr left, cmpop* ops, expr* comparators)
| Call(expr func, expr* args, keyword* keywords)
| Num(object n) -- a number as a PyObject.
| Str(string s) -- need to specify raw, unicode, etc?
| FormattedValue(expr value, int? conversion, expr? format_spec)
| JoinedStr(expr* values)
| Bytes(bytes s)
| NameConstant(singleton value)
| Ellipsis
| Constant(constant value)
-- the following expression can appear in assignment context
| Attribute(expr value, identifier attr, expr_context ctx)
| Subscript(expr value, slice slice, expr_context ctx)
| Starred(expr value, expr_context ctx)
| Name(identifier id, expr_context ctx)
| List(expr* elts, expr_context ctx)
| Tuple(expr* elts, expr_context ctx)
-- col_offset is the byte offset in the utf8 string the parser uses
attributes (int lineno, int col_offset)
expr_context = Load | Store | Del | AugLoad | AugStore | Param
slice = Slice(expr? lower, expr? upper, expr? step)
| ExtSlice(slice* dims)
| Index(expr value)
boolop = And | Or
operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift
| RShift | BitOr | BitXor | BitAnd | FloorDiv
unaryop = Invert | Not | UAdd | USub
cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
comprehension = (expr target, expr iter, expr* ifs, int is_async)
excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
attributes (int lineno, int col_offset)
arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults,
arg? kwarg, expr* defaults)
arg = (identifier arg, expr? annotation)
attributes (int lineno, int col_offset)
-- keyword arguments supplied to call (NULL identifier for **kwargs)
keyword = (identifier? arg, expr value)
-- import name with optional 'as' alias.
alias = (identifier name, identifier? asname)
withitem = (expr context_expr, expr? optional_vars)
}
ast Вспомогательные функции¶ast Helpers
Помимо классов узлов, модуль ast определяет следующие служебные функции и классы для обхода абстрактных синтаксических деревьев:
-
ast.parse(source, filename='<unknown>', mode='exec')¶ Разбирает исходный код в узел AST. Эквивалентно
compile(source, filename, mode, ast.PyCF_ONLY_AST).Предупреждение
Можно вызвать сбой интерпретатора Python с помощью достаточно большой/сложной строки из-за ограничений глубины стека в компиляторе AST Python.
-
ast.literal_eval(node_or_string)¶ Безопасно вычисляет узел выражения или строку, содержащую литерал Python или отображение контейнера. Предоставленная строка или узел могут состоять только из следующих литеральных структур Python: строки, байты, числа, кортежи, списки, словари, множества, логические значения и
None.Это можно использовать для безопасного вычисления строк, содержащих значения Python из ненадёжных источников, без необходимости разбирать значения самостоятельно. Он не способен вычислять произвольно сложные выражения, например с операторами или индексацией.
Предупреждение
Можно вызвать сбой интерпретатора Python с помощью достаточно большой/сложной строки из-за ограничений глубины стека в компиляторе AST Python.
Изменено в версии 3.2: Теперь допускает литералы байтов и множеств.
-
ast.get_docstring(node, clean=True)¶ Возвращает строку документации для данного узла (который должен быть узлом
FunctionDef,AsyncFunctionDef,ClassDefилиModule) илиNone, если у него нет строки документации. Если clean истинно, очищает отступы строки документации с помощьюinspect.cleandoc().Изменено в версии 3.5: Теперь поддерживается
AsyncFunctionDef.
-
ast.fix_missing_locations(node)¶ При компиляции дерева узлов с помощью
compile()компилятор ожидает атрибутыlinenoиcol_offsetдля каждого узла, который их поддерживает. Заполнять их вручную для сгенерированных узлов довольно утомительно, поэтому эта вспомогательная функция рекурсивно добавляет эти атрибуты, если их ещё нет, устанавливая их равными значениям родительского узла. Работает рекурсивно, начиная с node.
-
ast.increment_lineno(node, n=1)¶ Увеличить номер строки каждого узла в дереве, начиная с node, на n. Это полезно для «переноса кода» в другое место в файле.
-
ast.copy_location(new_node, old_node)¶ Скопировать местоположение в исходном коде (
linenoиcol_offset) из old_node в new_node, если возможно, и вернуть new_node.
-
ast.iter_fields(node)¶ Генерирует кортеж из
(fieldname, value)для каждого поля вnode._fields, которое присутствует в node.
-
ast.iter_child_nodes(node)¶ Генерирует все непосредственные дочерние узлы node, то есть все поля, являющиеся узлами, и все элементы полей, являющихся списками узлов.
-
ast.walk(node)¶ Рекурсивно генерирует все узлы-потомки в дереве, начиная с node (включая сам node), в произвольном порядке. Это полезно, если требуется только изменить узлы на месте, а контекст не важен.
-
class
ast.NodeVisitor¶ Базовый класс для обхода узлов, который обходит абстрактное синтаксическое дерево и вызывает функцию-посетитель для каждого найденного узла. Эта функция может вернуть значение, которое передаётся методом
visit().Этот класс предназначен для наследования; в подклассе добавляются методы-посетители.
-
visit(node)¶ Посещает узел. Реализация по умолчанию вызывает метод
self.visit_classname, где classname – имя класса узла, илиgeneric_visit(), если такой метод отсутствует.
-
generic_visit(node)¶ Этот посетитель вызывает
visit()для всех дочерних узлов.Обратите внимание, что дочерние узлы тех узлов, для которых определён собственный метод-посетитель, не будут посещены, если посетитель сам не вызовет
generic_visit()или не обойдёт их самостоятельно.
Не используйте
NodeVisitor, если требуется вносить изменения в узлы во время обхода. Для этого существует специальный посетитель (NodeTransformer), который допускает модификации.-
-
class
ast.NodeTransformer¶ Подкласс
NodeVisitor, который обходит абстрактное синтаксическое дерево и позволяет изменять узлы.Метод
NodeTransformerобходит AST и использует возвращаемое значение методов-посетителей для замены или удаления старого узла. Если возвращаемое значение метода-посетителя равноNone, узел будет удалён из своего местоположения; в противном случае он заменяется возвращённым значением. Возвращённое значение может быть исходным узлом – в этом случае замена не происходит.Вот пример преобразователя, который переписывает все обращения к именам (
foo) вdata['foo']:class RewriteName(NodeTransformer): def visit_Name(self, node): return Subscript( value=Name(id='data', ctx=Load()), slice=Index(value=Str(s=node.id)), ctx=node.ctx )
Имейте в виду: если у узла, с которым вы работаете, есть дочерние узлы, необходимо либо преобразовать дочерние узлы самостоятельно, либо сначала вызвать для узла метод
generic_visit().Для узлов, входящих в состав набора инструкций (это относится ко всем узлам инструкций), посетитель может также возвращать список узлов, а не один узел.
Если
NodeTransformerвводит новые узлы (не входившие в исходное дерево) без указания информации о расположении (например,lineno), следует вызватьfix_missing_locations()для нового поддерева, чтобы пересчитать информацию о расположении:tree = ast.parse('foo', mode='eval') new_tree = fix_missing_locations(RewriteName().visit(tree))
Обычно преобразователь используется следующим образом:
node = YourTransformer().visit(node)
-
ast.dump(node, annotate_fields=True, include_attributes=False)¶ Возвращает форматированный дамп дерева из node. Это в основном полезно для отладки. Если annotate_fields равно true (по умолчанию), возвращаемая строка будет показывать имена и значения полей. Если annotate_fields равно false, результирующая строка будет более компактной за счёт опускания однозначных имён полей. Атрибуты, такие как номера строк и смещения столбцов, по умолчанию не выводятся. Если это нужно, include_attributes можно установить в true.
См. также
Green Tree Snakes – внешний ресурс документации, содержащий подробную информацию о работе с AST Python.