ast — Деревья абстрактного синтаксиса
Исходный код: Lib/ast.py
Модуль ast помогает приложениям Python обрабатывать деревья абстрактной синтаксической грамматики Python. Сам абстрактный синтаксис может меняться с каждой версией Python; этот модуль помогает программно определить, как выглядит текущая грамматика.
Дерево абстрактного синтаксиса можно сгенерировать, передав ast.PyCF_ONLY_AST в качестве флага функции compile() или используя помощник parse() из этого модуля. Результатом будет дерево объектов, классы которых наследуются от ast.AST. Дерево абстрактного синтаксиса можно скомпилировать в объект кода Python, используя встроенную функцию compile().
1. Классы узлов
-
class ast.AST -
Это базовый класс для всех классов узлов AST. Фактические классы узлов наследуются от файла
Parser/Python.asdl, который приведен ниже. Они определены в модуле_astC и повторно экспортируются в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) -
2. Абстрактная грамматика
Абстрактная грамматика в настоящее время определена следующим образом:
-- ASDL's six builtin types are identifier, int, string, bytes, object, singleton
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)
-- 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?
| Bytes(bytes s)
| NameConstant(singleton value)
| Ellipsis
-- 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)
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)
}
3. Помощники ast
Помимо классов узлов, модуль ast определяет следующие вспомогательные функции и классы для обхода деревьев абстрактного синтаксиса:
-
ast.parse(source, filename='<unknown>', mode='exec') -
Парсит исходный код в узел AST. Эквивалентно
compile(source, filename, mode, ast.PyCF_ONLY_AST).
-
ast.literal_eval(node_or_string) -
Безопасно вычисляет узел выражения или строку, содержащую Python-литерал или представление контейнера. Предоставленная строка или узел могут содержать только следующие структуры Python-литералов: строки, байты, числа, кортежи, списки, словари, множества, булевы значения и
None.Это можно использовать для безопасного вычисления строк, содержащих значения Python из ненадежных источников, без необходимости самостоятельно разбирать значения. Он не способен вычислять произвольно сложные выражения, например, с операторами или индексацией.
Изменено в версии 3.2: Теперь поддерживает байты и множества.
-
ast.get_docstring(node, clean=True) -
Возвращает строку документации заданного узла node (который должен быть узлом
FunctionDef,ClassDefилиModule), илиNoneв случае отсутствия строки документации. Если clean равно True, отформатирует строку документации, используяinspect.cleandoc().
-
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 copy_location(Subscript( value=Name(id='data', ctx=Load()), slice=Index(value=Str(s=node.id)), ctx=node.ctx ), node)Обратите внимание, что если у узла, с которым вы работаете, есть дочерние узлы, вы должны либо преобразовать дочерние узлы самостоятельно, либо сначала вызвать метод
generic_visit()для узла.Для узлов, которые были частью коллекции операторов (это относится ко всем узлам операторов), посетитель также может вернуть список узлов вместо одного узла.
Обычно трансформатор используется следующим образом:
node = YourTransformer().visit(node)
-
ast.dump(node, annotate_fields=True, include_attributes=False) -
Возвращает отформатированный вывод дерева в узле node. Это в основном полезно для отладки. Возвращаемая строка отобразит имена и значения полей. Это делает код невозможным для оценки, поэтому, если нужна оценка, annotate_fields должно быть установлено в
False. Атрибуты, такие как номера строк и смещения столбцов, по умолчанию не выводятся. Если это нужно, include_attributes можно установить вTrue.
См. также
Зеленые древесные змеи, внешний ресурс документации, содержит подробную информацию о работе с Python AST.
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/ast.html