ast
— Абстрактные синтаксические деревья¶
Исходный код: Lib/ast.py
Модуль ast
помогает Python приложениям обрабатывать Python деревья
абстрактных синтаксических грамматик. Сам абстрактный синтаксис может
измениться с каждым выпуском Python’а; модуль помогает программно выяснить,
на какую текущая грамматика похожа.
Абстрактное синтаксическое дерево может быть создано путем передачи ast.PyCF_ONLY_AST
как флаг встроенной функции compile()
или с использованием parse()
хелпера, представленного в этом модуле. Результатом будет дерево объектов, чьи
классы наследуются от ast.AST
. Абстрактное синтаксическое дерево может
быть скомпилировано в объект Python кода с помощью встроенной функции
compile()
.
Node классы¶
-
class
ast.
AST
¶ Является базовым для всех классов нод AST. Фактические нод классы извлекаются из файла
Parser/Python.asdl
, которые воспроизводятся below. Они определены в C модуле_ast
и реэкспортируются вast
.В абстрактной грамматике (например, класс или
ast.expr
) для каждого левого символа определен одинast.stmt
. Кроме того, для каждого конструктора справа определен один класс; эти классы наследуют от классы для левосторонних деревьев. Например,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 байтами смещений первого и последнего маркеров, сгенерировавших ноду. Запиcь cмещения UTF-8 производится по причине внутреннеого использования анализатором UTF-8.Обратите внимание, что конечные позиции компилятора не требуются и поэтому являются необязательными. Конечное смещение after является последним символом, например, можно получить исходный сегмент однолинейной ноды выражения с помощью
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 интерпретатора с большой/сложной строкой из-за ограничений глубины стека Python’а компиляторв AST.
Изменено в версии 3.8: Добавлены
type_comments
,mode='func_type'
иfeature_version
.
-
ast.
literal_eval
(node_or_string)¶ Безопасная оценка узла выражения или строка, содержащего Python литерал или отображение контейнера. Предоставленный строка или узел может состоять только из следующих Python литеральных структур: строки, байт, чисел, кортежей, списков, знаков, наборов, булевов и
None
.Это может быть используемый для безопасной оценки строки, содержащих Python значения из ненадежных источников без необходимости разбора самих значений. Он не способен оценивать произвольно сложные выражения, например, с участием операторов или индексирования.
Предупреждение
Возможно крушение Python интерпретатора с большой/сложной строкой из-за ограничений глубины стека Python’а компиляторв AST.
Изменено в версии 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
, который присутствует в ноде.
-
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 и использовать возвращаемое значение методов посещения для замены или удаления старых нод. Если возвращаемое значение метода visitor является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)¶ Возвращение отформатированного дампа дерева нод. Бывает полезно для отладки. Если annotate_fields имеет значение True (по умолчанию), в возвращаемой строке будут отображаться имена и значения полей. Если annotate_fields является False, то строка результата будет более компактным, если не указать однозначные имена полей. По умолчанию такие атрибуты, как номера строк и смещения столбцов, не сбрасываются. Если это требуется, include_attributes можно установить значение в True.
См.также
Зеленые Древесные Змеи, внешний ресурс документации, имеет хорошие сведения о работе с Python AST.
ASTTokens аннотирует Python AST с позициями токенов и текста в исходном коде, который их сгенерировал. Это полезно для инструментов, которые делают преобразования исходного кода.
leoAst.py объединяет представления программ python на основе токенов и дерева синтаксического анализа, вставляя двухсторонние связи между токеныами и узлами ast.
LibCST анализирует код как конкретное синтаксическое дерево, которое выглядит как дерево ast и сохраняет все детали форматирования. Он полезен для создания приложений автоматизированного рефакторинга (codemod) и линтеров.
Parso — это парсер Python, который поддерживает восстановление ошибок и синтаксический анализ туда и обратно для различных версий Python (в нескольких версиях Python). Parso также может перечислить несколько синтаксических ошибок в вашем файле python.