> **Источник:** https://python-all.ru/3.0/library/ast.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# Абстрактные синтаксические деревья

Модуль `ast` помогает приложениям Python обрабатывать деревья грамматики абстрактного синтаксиса Python. Сам абстрактный синтаксис может меняться от версии к версии Python; этот модуль позволяет программно узнавать, как выглядит текущая грамматика.

Абстрактное синтаксическое дерево можно получить, передав `ast.PyCF_ONLY_AST` в качестве флага встроенной функции [`compile()`](https://python-all.ru/3.0/library/functions.html#compile) или с помощью вспомогательной функции [`parse()`](https://python-all.ru/3.0/library/ast.html#ast.parse) из этого модуля. Результатом будет дерево объектов, все классы которых наследуют от [`ast.AST`](https://python-all.ru/3.0/library/ast.html#ast.AST). Абстрактное синтаксическое дерево можно скомпилировать в объект кода Python с помощью встроенной функции [`compile()`](https://python-all.ru/3.0/library/functions.html#compile).

## Классы узлов

#### `[ast.AST]class ast.AST`

Это базовый класс для всех узлов AST. Фактические классы узлов порождаются из файла `Parser/Python.asdl`, который воспроизведён [*ниже*](https://python-all.ru/3.0/library/ast.html#abstract-grammar). Они определены в C-модуле `_ast` и повторно экспортируются в `ast`.

Для каждого символа левой части абстрактной грамматики определён один класс (например, `ast.stmt` или `ast.expr`). Кроме того, для каждого конструктора правой части определён один класс; эти классы наследуют от классов левой части. Например, `ast.BinOp` наследует от `ast.expr`. Для правил вывода с альтернативами (так называемых «сумм») класс левой части является абстрактным: создаются только экземпляры конкретных конструкторных узлов.

#### `[ast.AST._fields]_fields`

Каждый конкретный класс имеет атрибут [`_fields`](https://python-all.ru/3.0/library/ast.html#ast.AST._fields), который содержит имена всех дочерних узлов.

Каждый экземпляр конкретного класса имеет по одному атрибуту для каждого дочернего узла, тип которого определён грамматикой. Например, экземпляры `ast.BinOp` имеют атрибут `left` типа `ast.expr`.

Если эти атрибуты помечены как необязательные в грамматике (с помощью вопросительного знака), их значением может быть `None`. Если атрибуты могут иметь ноль или более значений (помечены звёздочкой), значения представляются в виде списков Python. Все возможные атрибуты должны присутствовать и иметь допустимые значения при компиляции AST с помощью [`compile()`](https://python-all.ru/3.0/library/functions.html#compile).

#### `[ast.AST.lineno]lineno`

#### `col_offset`

Экземпляры подклассов

`ast.expr`

и

`ast.stmt`

имеют атрибуты

[`lineno`](https://python-all.ru/3.0/library/ast.html#ast.AST.lineno)

и

[`col_offset`](https://python-all.ru/3.0/library/ast.html#ast.AST.col_offset)

.

[`lineno`](https://python-all.ru/3.0/library/ast.html#ast.AST.lineno)

– это номер строки исходного текста (нумерация с 1, так что первая строка – строка 1), а

[`col_offset`](https://python-all.ru/3.0/library/ast.html#ast.AST.col_offset)

– это смещение в байтах UTF-8 первого токена, породившего узел. Смещение в UTF-8 записывается, потому что парсер использует UTF-8 внутри.

Конструктор класса `ast.T` разбирает свои аргументы следующим образом:

- Если есть позиционные аргументы, их должно быть столько же, сколько элементов в `T._fields`; они будут присвоены как атрибуты с этими именами.
- Если есть ключевые аргументы, они установят атрибуты с теми же именами в указанные значения.

Например, чтобы создать и заполнить узел `ast.UnaryOp`, можно использовать

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

или более компактный

```python
node = ast.UnaryOp(ast.USub(), ast.Num(5, lineno=0, col_offset=0),
                   lineno=0, col_offset=0)
```

## Абстрактная грамматика

Модуль определяет строковую константу `__version__`, которая содержит десятичный номер ревизии Subversion файла, показанного ниже.

Абстрактная грамматика в настоящее время определяется следующим образом:

```python
-- ASDL's four builtin types are identifier, int, string, object

module Python version "$Revision: 67644 $"
{
	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)
	      | ClassDef(identifier name, 
			 expr* bases,
			 keyword* keywords,
			 expr? starargs,
			 expr? kwargs,
			 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)
	      | While(expr test, stmt* body, stmt* orelse)
	      | If(expr test, stmt* body, stmt* orelse)
	      | With(expr context_expr, expr? optional_vars, stmt* body)

	      | Raise(expr? exc, expr? cause)
	      | TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
	      | TryFinally(stmt* body, 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
	     | Yield(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,
			 expr? starargs, expr? kwargs)
	     | Num(object n) -- a number as a PyObject.
	     | Str(string s) -- need to specify raw, unicode, etc?
	     | Bytes(string s)
	     | Ellipsis
	     -- other literals? bools?

	     -- 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 | 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)

	-- not sure what to call the first argument for raise and except
	excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
	                attributes (int lineno, int col_offset)

	arguments = (arg* args, identifier? vararg, expr? varargannotation,
                     arg* kwonlyargs, identifier? kwarg,
                     expr? kwargannotation, expr* defaults,
                     expr* kw_defaults)
	arg = (identifier arg, expr? annotation)

        -- keyword arguments supplied to call
        keyword = (identifier arg, expr value)

        -- import name with optional 'as' alias.
        alias = (identifier name, identifier? asname)
}
```

## `ast` Вспомогательные функции

Помимо классов узлов, модуль `ast` определяет следующие вспомогательные функции и классы для обхода абстрактных синтаксических деревьев:

#### `[ast.parse]ast.parse(expr, filename='<unknown>', mode='exec')`

Разбирает выражение в узел AST. Эквивалентно

`compile(expr, filename, mode, ast.PyCF_ONLY_AST)`

.

#### `[ast.literal_eval]ast.literal_eval(node_or_string)`

Безопасно вычисляет узел выражения или строку, содержащую выражение Python. Предоставленная строка или узел может содержать только следующие литеральные структуры Python: строки, числа, кортежи, списки, словари, логические значения и `None`.

Это можно использовать для безопасного вычисления строк, содержащих выражения Python из ненадёжных источников, без необходимости самостоятельно их разбирать.

#### `[ast.get_docstring]ast.get_docstring(node, clean=True)`

Возвращает docstring заданного

*node*

(который должен быть узлом

`FunctionDef`

,

`ClassDef`

или

`Module`

), или

`None`

, если docstring нет. Если

*clean*

равно True, очищает отступы docstring с помощью

[`inspect.cleandoc()`](https://python-all.ru/3.0/library/inspect.html#inspect.cleandoc)

.

#### `[ast.fix_missing_locations]ast.fix_missing_locations(node)`

При компиляции дерева узлов с помощью

[`compile()`](https://python-all.ru/3.0/library/functions.html#compile)

компилятор ожидает атрибуты

`lineno`

и

`col_offset`

для каждого узла, который их поддерживает. Заполнять их вручную для сгенерированных узлов довольно утомительно, поэтому эта вспомогательная функция рекурсивно добавляет эти атрибуты, если они ещё не установлены, присваивая им значения родительского узла. Она работает рекурсивно, начиная с

*node*

.

#### `[ast.increment_lineno]ast.increment_lineno(node, n=1)`

Увеличить номер строки каждого узла в дереве, начиная с

*node*

, на

*n*

. Это полезно для «переноса кода» в другое место в файле.

#### `[ast.copy_location]ast.copy_location(new_node, old_node)`

Копирует информацию о положении в исходном коде (

`lineno`

и

`col_offset`

) из

*old\_node*

в

*new\_node*

, если это возможно, и возвращает

*new\_node*

.

#### `[ast.iter_fields]ast.iter_fields(node)`

Генерирует кортеж

`(fieldname, value)`

для каждого поля из

`node._fields`

, которое присутствует в

*node*

.

#### `[ast.iter_child_nodes]ast.iter_child_nodes(node)`

Генерирует все непосредственные дочерние узлы

*node*

, то есть все поля, являющиеся узлами, и все элементы полей, являющихся списками узлов.

#### `[ast.walk]ast.walk(node)`

Рекурсивно выдаёт все дочерние узлы

*node*

в произвольном порядке. Это полезно, если требуется только изменить узлы на месте, не заботясь о контексте.

#### `[ast.NodeVisitor]class ast.NodeVisitor`

Базовый класс для обхода абстрактного синтаксического дерева, который вызывает функцию посетителя для каждого найденного узла. Эта функция может возвращать значение, которое передаётся методом [`visit()`](https://python-all.ru/3.0/library/ast.html#ast.NodeVisitor.visit).

Этот класс предназначен для наследования; в подклассе добавляются методы-посетители.

#### `[ast.NodeVisitor.visit]visit(node)`

Посещает узел. Реализация по умолчанию вызывает метод

`self.visit_classname`

, где

*classname*

– это имя класса узла, или

[`generic_visit()`](https://python-all.ru/3.0/library/ast.html#ast.NodeVisitor.generic_visit)

, если такой метод не существует.

#### `[ast.NodeVisitor.generic_visit]generic_visit(node)`

Этот обходчик вызывает [`visit()`](https://python-all.ru/3.0/library/ast.html#ast.NodeVisitor.visit) для всех дочерних узлов данного узла.

Обратите внимание, что дочерние узлы узлов, для которых определён пользовательский метод обхода, не будут посещены, если обходчик не вызовет [`generic_visit()`](https://python-all.ru/3.0/library/ast.html#ast.NodeVisitor.generic_visit) или не посетит их самостоятельно.

Не используйте [`NodeVisitor`](https://python-all.ru/3.0/library/ast.html#ast.NodeVisitor) для внесения изменений в узлы во время обхода. Для этого существует специальный обходчик ([`NodeTransformer`](https://python-all.ru/3.0/library/ast.html#ast.NodeTransformer)), позволяющий вносить изменения.

#### `[ast.NodeTransformer]class ast.NodeTransformer`

Подкласс [`NodeVisitor`](https://python-all.ru/3.0/library/ast.html#ast.NodeVisitor), который обходит абстрактное синтаксическое дерево и позволяет изменять узлы.

[`NodeTransformer`](https://python-all.ru/3.0/library/ast.html#ast.NodeTransformer) обходит AST и использует возвращаемое значение методов обхода для замены или удаления старого узла. Если возвращаемое значение метода обхода равно `None`, узел удаляется из своего местоположения; в противном случае он заменяется возвращаемым значением. Возвращаемое значение может быть исходным узлом, и в этом случае замена не производится.

Вот пример преобразователя, который заменяет все обращения к имени (`foo`) на `data['foo']`:

```python
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()` для этого узла.

Для узлов, входящих в состав набора инструкций (это относится ко всем узлам инструкций), посетитель может также возвращать список узлов, а не один узел.

Обычно преобразователь используется следующим образом:

```python
node = YourTransformer().visit(node)
```

#### `[ast.dump]ast.dump(node, annotate_fields=True, include_attributes=False)`

Возвращает форматированное представление дерева в виде дампа из объекта

*node*

. Это в основном полезно для целей отладки. Возвращаемая строка покажет имена и значения полей. Это делает код невозможным для выполнения, поэтому если требуется выполнимость,

*annotate\_fields*

должен быть установлен в False. Атрибуты, такие как номера строк и смещения столбцов, по умолчанию не выводятся. Если это нужно,

*include\_attributes*

можно установить в

`True`

.
