ast — Абстрактные синтаксические деревья
Исходный код: Lib/ast.py
Модуль ast помогает приложениям Python обрабатывать деревья абстрактной синтаксической грамматики Python. Сама абстрактная синтаксическая грамматика может изменяться с каждой версией Python; этот модуль помогает программно определить, как выглядит текущая грамматика.
Абстрактное синтаксическое дерево можно сгенерировать, передав ast.PyCF_ONLY_AST в качестве флага встроенной функции compile() или используя вспомогательную функцию parse() из этого модуля. Результатом будет дерево объектов, классы которых наследуются от ast.AST. Абстрактное синтаксическое дерево можно скомпилировать в объект кода Python, используя встроенную функцию compile().
Классы узлов
-
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 -
end_lineno -
end_col_offset -
Экземпляры классов
ast.exprиast.stmtимеют атрибутыlineno,col_offset,linenoиcol_offset. Атрибутыlinenoиend_linenoпредставляют номера первой и последней строки в тексте исходного кода (с индексацией с 1, первая строка — строка 1), а атрибутыcol_offsetиend_col_offset— соответствующие смещения байтов в кодировке UTF-8 для первых и последних токенов, которые сгенерировали узел. Смещение в UTF-8 записывается, потому что парсер использует UTF-8 в своей работе.Обратите внимание, что конечные позиции не требуются компилятором и поэтому являются необязательными. Конечное смещение находится после последнего символа, например, вы можете получить сегмент исходного кода узла однострочного выражения с помощью
source_line[node.col_offset : node.end_col_offset].
Конструктор класса
ast.Tанализирует его аргументы следующим образом:- Если есть позиционные аргументы, их должно быть столько же, сколько элементов в
T._fields; они будут присвоены в качестве атрибутов с соответствующими именами. - Если есть именованные аргументы, они будут устанавливать атрибуты с теми же именами в соответствии с указанными значениями.
Например, чтобы создать и заполнить узел
ast.UnaryOp, можно использоватьnode = ast.UnaryOp() node.op = ast.USub() node.operand = ast.Constant() node.operand.value = 5 node.operand.lineno = 0 node.operand.col_offset = 0 node.lineno = 0 node.col_offset = 0
или более компактный вариант
node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0), lineno=0, col_offset=0) -
Изменено в версии 3.8: Класс ast.Constant теперь используется для всех констант.
Устарело начиная с версии 3.8: Старые классы ast.Num, ast.Str, ast.Bytes, ast.NameConstant и ast.Ellipsis всё ещё доступны, но они будут удалены в будущих версиях Python. В то же время, их создание вернёт экземпляр другого класса.
Абстрактная грамматика
Абстрактная грамматика в настоящее время определена следующим образом:
-- ASDL's 5 builtin types are:
-- identifier, int, string, object, constant
module Python
{
mod = Module(stmt* body, type_ignore *type_ignores)
| Interactive(stmt* body)
| Expression(expr body)
| FunctionType(expr* argtypes, expr returns)
-- 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,
string? type_comment)
| AsyncFunctionDef(identifier name, arguments args,
stmt* body, expr* decorator_list, expr? returns,
string? type_comment)
| ClassDef(identifier name,
expr* bases,
keyword* keywords,
stmt* body,
expr* decorator_list)
| Return(expr? value)
| Delete(expr* targets)
| Assign(expr* targets, expr value, string? type_comment)
| 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, string? type_comment)
| AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(withitem* items, stmt* body, string? type_comment)
| AsyncWith(withitem* items, stmt* body, string? type_comment)
| 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, int? end_lineno, int? end_col_offset)
-- BoolOp() can use left & right?
expr = BoolOp(boolop op, expr* values)
| NamedExpr(expr target, expr value)
| 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)
| FormattedValue(expr value, int? conversion, expr? format_spec)
| JoinedStr(expr* values)
| Constant(constant value, string? kind)
-- 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, int? end_lineno, int? end_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, int? end_lineno, int? end_col_offset)
arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs,
expr* kw_defaults, arg? kwarg, expr* defaults)
arg = (identifier arg, expr? annotation, string? type_comment)
attributes (int lineno, int col_offset, int? end_lineno, int? end_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)
type_ignore = TypeIgnore(int lineno, string tag)
}
Помощники AST
Помимо классов узлов, модуль ast определяет эти служебные функции и классы для обхода абстрактных синтаксических деревьев:
-
ast.parse(source, filename='<unknown>', mode='exec', *, type_comments=False, feature_version=None) -
Разбор исходного кода в узел AST. Эквивалентно
compile(source, filename, mode, ast.PyCF_ONLY_AST).Если
type_comments=Trueзадано, анализатор модифицируется для проверки и возврата комментариев типов, как указано в PEP 484 и PEP 526. Это эквивалентно добавлениюast.PyCF_TYPE_COMMENTSк флагам, переданнымcompile(). Это будет сообщать об ошибках синтаксиса для неправильно размещенных комментариев типов. Без этого флага комментарии типов будут игнорироваться, и полеtype_commentдля выбранных узлов AST всегда будетNone. Кроме того, расположения комментариев# type: ignoreбудут возвращены как атрибутtype_ignoresузлаModule(в противном случае это всегда пустой список).Кроме того, если
modeравно'func_type', синтаксис входных данных изменяется, чтобы соответствовать «комментариям типов сигнатуры» PEP 484, например,(str, int) -> List[str].Также, установка
feature_versionв кортеж(major, minor)попытается выполнить разбор с помощью грамматики указанной версии Python. В настоящее времяmajorдолжно быть равно3. Например, установкаfeature_version=(3, 4)позволит использоватьasyncиawaitв качестве имён переменных. Наименьшая поддерживаемая версия —(3, 4); наибольшая —sys.version_info[0:2].Предупреждение
Возможна ошибка интерпретатора Python при достаточно большой/сложной строке из-за ограничений глубины стека в компиляторе AST Python.
Изменено в версии 3.8: Добавлены
type_comments,mode='func_type'иfeature_version.
-
ast.literal_eval(node_or_string) -
Безопасно оценить узел выражения или строку, содержащую Python-литерал или отображение контейнера. Предоставленная строка или узел могут содержать только следующие структуры Python-литералов: строки, байты, числа, кортежи, списки, словари, множества, булевы значения и
None.Это можно использовать для безопасной оценки строк, содержащих значения Python из ненадежных источников, без необходимости их самостоятельного разбора. Она не способна оценивать произвольно сложные выражения, например, включающие операторы или индексирование.
Предупреждение
Возможна ошибка интерпретатора Python при достаточно большой/сложной строке из-за ограничений глубины стека в компиляторе AST Python.
Изменено в версии 3.2: Теперь поддерживает байты и множества.
-
ast.get_docstring(node, clean=True) -
Возвращает строку документации данного узла node (который должен быть узлом
FunctionDef,AsyncFunctionDef,ClassDef, илиModule), илиNoneесли у него нет строки документации. Если clean истинно, отформатировать отступ строки документации с помощьюinspect.cleandoc().Изменено в версии 3.5:
AsyncFunctionDefтеперь поддерживается.
-
ast.get_source_segment(source, node, *, padded=False) -
Получить фрагмент исходного кода source, который сгенерировал node. Если некоторая информация о расположении (
lineno,end_lineno,col_offset, илиend_col_offset) отсутствует, возвращаетсяNone.Если padded равно
True, первая строка многострочного оператора будет дополнена пробелами, чтобы соответствовать ее первоначальному положению.Введено в версии 3.8.
-
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,end_lineno, иend_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), который позволяет вносить изменения.Устарело начиная с версии 3.8: Методы
visit_Num(),visit_Str(),visit_Bytes(),visit_NameConstant()иvisit_Ellipsis()устарели и не будут вызываться в будущих версиях Python. Добавьте методvisit_Constant()для обработки всех постоянных узлов. -
-
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=Constant(value=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 истинно (по умолчанию), возвращаемая строка покажет имена и значения полей. Если annotate_fields ложно, результирующая строка будет более компактной, опуская недвусмысленные имена полей. Атрибуты, такие как номера строк и смещения столбцов, по умолчанию не выводятся. Если это нужно, можно установить include_attributes в значение true.
См. также
Зелёные древесные змеи, внешний ресурс документации, содержит подробные сведения о работе с Python AST.
ASTTokens добавляет в Python AST позиции токенов и текст из исходного кода, который их породил. Это полезно для инструментов, выполняющих преобразования исходного кода.
leoAst.py объединяет основанные на токенах и на синтаксических деревьях представления python-программ, вставляя двусторонние связи между токенами и узлами ast.
LibCST анализирует код как дерево конкретной синтаксической структуры (Concrete Syntax Tree), которое выглядит как дерево ast и сохраняет все детали форматирования. Это полезно для создания автоматизированных приложений для переработки кода (codemod) и линтеров.
Parso — это Python-парсер, который поддерживает восстановление ошибок и двусторонний разбор для разных версий Python (в нескольких версиях Python). Parso также может перечислить несколько синтаксических ошибок в вашем python-файле.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/ast.html