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

---

# 33. Пакет компилятора Python

Устарело с версии 2.6: Пакет [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) удалён в Python 3.

Пакет компилятора Python – это инструмент для анализа исходного кода Python и генерации байт-кода Python. Компилятор содержит библиотеки для построения абстрактного синтаксического дерева из исходного кода Python и для генерации Python [байт-кода](https://python-all.ru/2.7/glossary.html#term-bytecode) из дерева.

Пакет [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) – это транслятор исходного кода Python в байт-код, написанный на Python. Он использует встроенный парсер и стандартный модуль [`parser`](https://python-all.ru/2.7/library/parser.html#module-parser) для построения конкретного синтаксического дерева. Это дерево используется для построения абстрактного синтаксического дерева (AST) и затем байт-кода Python.

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

В этой главе объясняется, как работают различные компоненты пакета [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler). В ней сочетаются справочный материал и учебное пособие.

## 33.1. Базовый интерфейс

Верхний уровень пакета определяет четыре функции. При импорте [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) будут доступны эти функции и набор модулей, содержащихся в пакете.

#### `compiler.parse(buf)`

Возвращает абстрактное синтаксическое дерево для исходного кода Python в *buf*. Функция возбуждает [`SyntaxError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.SyntaxError), если в исходном коде есть ошибка. Возвращаемое значение – экземпляр `compiler.ast.Module`, содержащий дерево.

#### `compiler.parseFile(path)`

Возвращает абстрактное синтаксическое дерево для исходного кода Python в файле, указанном параметром *path*. Эквивалентно `parse(open(path).read())`.

#### `compiler.walk(ast, visitor[, verbose])`

Выполняет прямой обход абстрактного синтаксического дерева *ast*. Вызывает соответствующий метод экземпляра *visitor* для каждого встреченного узла.

#### `compiler.compile(source, filename, mode, flags=None, dont_inherit=None)`

Компилирует строку *source* (модуль Python, инструкцию или выражение) в объект кода, который может быть выполнен инструкцией exec или [`eval()`](https://python-all.ru/2.7/library/functions.html#eval). Эта функция является заменой встроенной функции [`compile()`](https://python-all.ru/2.7/library/functions.html#compile).

Параметр *filename* будет использоваться для сообщений об ошибках времени выполнения.

Параметр *mode* должен быть равен ‘exec’ для компиляции модуля, ‘single’ для компиляции одной (интерактивной) инструкции или ‘eval’ для компиляции выражения.

Аргументы *flags* и *dont\_inherit* влияют на операторы, связанные с future, но пока не поддерживаются.

#### `compiler.compileFile(source)`

Компилирует файл *source* и генерирует файл .pyc.

Пакет [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) содержит следующие модули: [`ast`](https://python-all.ru/2.7/library/ast.html#module-ast), `consts`, `future`, `misc`, `pyassem`, `pycodegen`, `symbols`, `transformer` и `visitor`.

## 33.2. Ограничения

В проверке ошибок пакета компилятора есть некоторые проблемы. Интерпретатор обнаруживает синтаксические ошибки в два отдельных этапа. Один набор ошибок обнаруживается парсером интерпретатора, другой – компилятором. Пакет компилятора полагается на парсер интерпретатора, поэтому первый этап проверки ошибок он получает «бесплатно». Второй этап он реализует самостоятельно, и эта реализация неполна. Например, пакет компилятора не возбуждает ошибку, если имя встречается более одного раза в списке аргументов: `def f(x, x): ...`

Будущая версия компилятора должна исправить эти проблемы.

## 33.3. Абстрактный синтаксис Python

Модуль [`compiler.ast`](https://python-all.ru/2.7/library/compiler.html#module-compiler.ast) определяет абстрактный синтаксис для Python. В абстрактном синтаксическом дереве каждый узел представляет синтаксическую конструкцию. Корнем дерева является объект `Module`.

Абстрактный синтаксис предоставляет интерфейс более высокого уровня для разобранного исходного кода Python. Модуль [`parser`](https://python-all.ru/2.7/library/parser.html#module-parser) и компилятор, написанный на C для интерпретатора Python, используют конкретное синтаксическое дерево. Конкретный синтаксис тесно связан с описанием грамматики, используемым парсером Python. Вместо одного узла для конструкции часто существует несколько уровней вложенных узлов, которые вводятся правилами приоритета Python.

Абстрактное синтаксическое дерево создаётся модулем `compiler.transformer`. Трансформер полагается на встроенный парсер Python для генерации конкретного синтаксического дерева. Из конкретного дерева он генерирует абстрактное синтаксическое дерево.

Модуль `transformer` был создан Грегом Стейном и Биллом Таттом для экспериментального компилятора из Python в C. Текущая версия содержит ряд модификаций и улучшений, но основная форма абстрактного синтаксиса и трансформера принадлежит Стейну и Татту.

### 33.3.1. Узлы AST

Модуль [`compiler.ast`](https://python-all.ru/2.7/library/compiler.html#module-compiler.ast) генерируется из текстового файла, описывающего каждый тип узла и его элементы. Каждый тип узла представлен классом, наследующим от абстрактного базового класса [`compiler.ast.Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) и определяющим набор именованных атрибутов для дочерних узлов.

#### `class compiler.ast.Node`

Экземпляры [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) создаются автоматически генератором парсера. Рекомендуемый интерфейс для конкретных экземпляров [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) – использовать публичные атрибуты для доступа к дочерним узлам. Публичный атрибут может быть привязан к одному узлу или к последовательности узлов в зависимости от типа [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node). Например, атрибут `bases` узла `Class` привязан к списку узлов базовых классов, а атрибут `doc` привязан к одному узлу.

Каждый экземпляр [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) имеет атрибут `lineno`, который может быть `None`. XXX Непонятно, по каким правилам узлы будут содержать полезный lineno.

Все объекты [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) имеют следующие методы:

#### `getChildren()`

Возвращает плоский список дочерних узлов и объектов в порядке их появления. А именно, порядок узлов соответствует порядку, в котором они встречаются в грамматике Python. Не все дочерние элементы являются экземплярами [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node). Например, имена функций и классов – обычные строки.

#### `getChildNodes()`

Возвращает плоский список дочерних узлов в порядке их появления. Этот метод подобен [`getChildren()`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node.getChildren), за исключением того, что возвращает только те дочерние элементы, которые являются экземплярами [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node).

Два примера иллюстрируют общую структуру классов [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node). Оператор [`while`](https://python-all.ru/2.7/reference/compound_stmts.html#while) определяется следующим грамматическим правилом:

```python
while_stmt:     "while" expression ":" suite
               ["else" ":" suite]
```

Узел `While` имеет три атрибута: [`test`](https://python-all.ru/2.7/library/test.html#module-test), `body` и `else_`. (Если естественное имя атрибута также является зарезервированным словом Python, его нельзя использовать в качестве имени атрибута. К слову добавляется символ подчёркивания, чтобы получить допустимый идентификатор, отсюда `else_` вместо [`else`](https://python-all.ru/2.7/reference/compound_stmts.html#else).)

Оператор [`if`](https://python-all.ru/2.7/reference/compound_stmts.html#if) более сложен, поскольку может включать несколько проверок.

```python
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
```

Узел `If` определяет только два атрибута: `tests` и `else_`. Атрибут `tests` представляет собой последовательность пар «проверочное выражение, соответствующее тело». По одной паре для каждого предложения [`if`](https://python-all.ru/2.7/reference/compound_stmts.html#if)/[`elif`](https://python-all.ru/2.7/reference/compound_stmts.html#elif). Первый элемент пары – проверочное выражение. Второй элемент – узел `Stmt`, содержащий код, который выполняется, если проверка истинна.

Метод `getChildren()` объекта `If` возвращает плоский список дочерних узлов. Если имеется три предложения [`if`](https://python-all.ru/2.7/reference/compound_stmts.html#if)/[`elif`](https://python-all.ru/2.7/reference/compound_stmts.html#elif) и нет предложения [`else`](https://python-all.ru/2.7/reference/compound_stmts.html#else), то `getChildren()` вернёт список из шести элементов: первое проверочное выражение, первый `Stmt`, второе проверочное выражение и так далее.

В следующей таблице перечислены все подклассы [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node), определённые в [`compiler.ast`](https://python-all.ru/2.7/library/compiler.html#module-compiler.ast), и все открытые атрибуты, доступные у их экземпляров. Значения большинства атрибутов сами являются экземплярами [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) или последовательностями экземпляров. Если значение не является экземпляром, тип указывается в комментарии. Атрибуты перечислены в порядке, в котором они возвращаются методами `getChildren()` и `getChildNodes()`.

| Тип узла | Атрибут | Значение |
| --- | --- | --- |
| `Add` | `left` | левый операнд |
|  | `right` | правый операнд |
| `And` | `nodes` | список операндов |
| `AssAttr` |  | *атрибут как цель присваивания* |
|  | `expr` | выражение слева от точки |
|  | `attrname` | имя атрибута, строка |
|  | `flags` | XXX |
| `AssList` | `nodes` | список элементов списка, которым присваивается значение |
| `AssName` | `name` | имя, которому присваивается значение |
|  | `flags` | XXX |
| `AssTuple` | `nodes` | список элементов кортежа, которым присваивается значение |
| `Assert` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) | проверяемое выражение |
|  | `fail` | значение [`AssertionError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AssertionError) |
| `Assign` | `nodes` | список целей присваивания, по одной на знак равенства |
|  | `expr` | присваиваемое значение |
| `AugAssign` | `node` |  |
|  | `op` |  |
|  | `expr` |  |
| `Backquote` | `expr` |  |
| `Bitand` | `nodes` |  |
| `Bitor` | `nodes` |  |
| `Bitxor` | `nodes` |  |
| `Break` |  |  |
| `CallFunc` | `node` | выражение для вызываемого объекта |
|  | `args` | список аргументов |
|  | `star_args` | значение расширенного \*-аргумента |
|  | `dstar_args` | значение расширенного \*\*-аргумента |
| `Class` | `name` | имя класса, строка |
|  | `bases` | список базовых классов |
|  | `doc` | строка документации, строка или `None` |
|  | [`code`](https://python-all.ru/2.7/library/code.html#module-code) | тело инструкции класса |
| `Compare` | `expr` |  |
|  | `ops` |  |
| `Const` | `value` |  |
| `Continue` |  |  |
| `Decorators` | `nodes` | Список выражений декораторов функции |
| `Dict` | `items` |  |
| `Discard` | `expr` |  |
| `Div` | `left` |  |
|  | `right` |  |
| [`Ellipsis`](https://python-all.ru/2.7/library/constants.html#Ellipsis) |  |  |
| `Expression` | `node` |  |
| `Exec` | `expr` |  |
|  | [`locals`](https://python-all.ru/2.7/library/functions.html#locals) |  |
|  | [`globals`](https://python-all.ru/2.7/library/functions.html#globals) |  |
| `FloorDiv` | `left` |  |
|  | `right` |  |
| `For` | `assign` |  |
|  | `list` |  |
|  | `body` |  |
|  | `else_` |  |
| `From` | `modname` |  |
|  | `names` |  |
| `Function` | `decorators` | `Decorators` или `None` |
|  | `name` | имя, используемое в def, строка |
|  | `argnames` | список имён аргументов в виде строк |
|  | `defaults` | список значений по умолчанию |
|  | `flags` | xxx |
|  | `doc` | строка документации, строка или `None` |
|  | [`code`](https://python-all.ru/2.7/library/code.html#module-code) | тело функции |
| `GenExpr` | [`code`](https://python-all.ru/2.7/library/code.html#module-code) |  |
| `GenExprFor` | `assign` |  |
|  | [`iter`](https://python-all.ru/2.7/library/functions.html#iter) |  |
|  | `ifs` |  |
| `GenExprIf` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) |  |
| `GenExprInner` | `expr` |  |
|  | `quals` |  |
| `Getattr` | `expr` |  |
|  | `attrname` |  |
| `Global` | `names` |  |
| `If` | `tests` |  |
|  | `else_` |  |
| `Import` | `names` |  |
| `Invert` | `expr` |  |
| `Keyword` | `name` |  |
|  | `expr` |  |
| `Lambda` | `argnames` |  |
|  | `defaults` |  |
|  | `flags` |  |
|  | [`code`](https://python-all.ru/2.7/library/code.html#module-code) |  |
| `LeftShift` | `left` |  |
|  | `right` |  |
| `List` | `nodes` |  |
| `ListComp` | `expr` |  |
|  | `quals` |  |
| `ListCompFor` | `assign` |  |
|  | `list` |  |
|  | `ifs` |  |
| `ListCompIf` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) |  |
| `Mod` | `left` |  |
|  | `right` |  |
| `Module` | `doc` | строка документации, строка или `None` |
|  | `node` | тело модуля, `Stmt` |
| `Mul` | `left` |  |
|  | `right` |  |
| `Name` | `name` |  |
| `Not` | `expr` |  |
| `Or` | `nodes` |  |
| `Pass` |  |  |
| `Power` | `left` |  |
|  | `right` |  |
| `Print` | `nodes` |  |
|  | `dest` |  |
| `Printnl` | `nodes` |  |
|  | `dest` |  |
| `Raise` | `expr1` |  |
|  | `expr2` |  |
|  | `expr3` |  |
| `Return` | `value` |  |
| `RightShift` | `left` |  |
|  | `right` |  |
| `Slice` | `expr` |  |
|  | `flags` |  |
|  | `lower` |  |
|  | `upper` |  |
| `Sliceobj` | `nodes` | список инструкций |
| `Stmt` | `nodes` |  |
| `Sub` | `left` |  |
|  | `right` |  |
| `Subscript` | `expr` |  |
|  | `flags` |  |
|  | `subs` |  |
| `TryExcept` | `body` |  |
|  | `handlers` |  |
|  | `else_` |  |
| `TryFinally` | `body` |  |
|  | `final` |  |
| `Tuple` | `nodes` |  |
| `UnaryAdd` | `expr` |  |
| `UnarySub` | `expr` |  |
| `While` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) |  |
|  | `body` |  |
|  | `else_` |  |
| `With` | `expr` |  |
|  | [`vars`](https://python-all.ru/2.7/library/functions.html#vars) |  |
|  | `body` |  |
| `Yield` | `value` |  |

### 33.3.2. Узлы присваивания

Существует набор узлов, используемых для представления присваиваний. Каждая инструкция присваивания в исходном коде становится одним узлом `Assign` в AST. Атрибут `nodes` представляет собой список, содержащий по одному узлу для каждой цели присваивания. Это необходимо, поскольку присваивание может быть цепочкой, например `a = b = 2`. Каждый [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) в списке будет одним из следующих классов: `AssAttr`, `AssList`, `AssName` или `AssTuple`.

Каждый узел цели присваивания будет описывать тип объекта, которому выполняется присваивание: `AssName` для простого имени, например `a = 1`. `AssAttr` для присваивания атрибута, например `a.x = 1`. `AssList` и `AssTuple` для расширения списка и кортежа соответственно, например `a, b, c = a_tuple`.

Узлы целей присваивания также имеют атрибут `flags`, который указывает, используется ли узел для присваивания или в инструкции удаления. `AssName` также используется для представления инструкции удаления, например `del x`.

Когда выражение содержит несколько ссылок на атрибуты, инструкция присваивания или удаления будет содержать только один узел `AssAttr` – для последней ссылки на атрибут. Остальные ссылки на атрибуты будут представлены как узлы `Getattr` в атрибуте `expr` экземпляра `AssAttr`.

### 33.3.3. Примеры

В этом разделе приведено несколько простых примеров AST для исходного кода Python. Примеры демонстрируют, как использовать функцию `parse()`, как выглядит repr AST и как получать доступ к атрибутам узла AST.

Первый модуль определяет одну функцию. Предположим, он сохранён в `doublelib.py`.

```python
"""Это пример модуля.

Это докстринг.
"""

def double(x):
    "Return twice the argument"
    return x * 2
```

В приведённом ниже сеансе интерактивного интерпретатора я переформатировал длинные представления AST для удобочитаемости. Представления AST используют неполные имена классов. Если вы хотите создать экземпляр на основе представления, необходимо импортировать имена классов из модуля [`compiler.ast`](https://python-all.ru/2.7/library/compiler.html#module-compiler.ast).

```python
>>> import compiler
>>> mod = compiler.parseFile("doublelib.py")
>>> mod
Module('This is an example module.\n\nThis is the docstring.\n',
       Stmt([Function(None, 'double', ['x'], [], 0,
                      'Return twice the argument',
                      Stmt([Return(Mul((Name('x'), Const(2))))]))]))
>>> from compiler.ast import *
>>> Module('This is an example module.\n\nThis is the docstring.\n',
...    Stmt([Function(None, 'double', ['x'], [], 0,
...                   'Return twice the argument',
...                   Stmt([Return(Mul((Name('x'), Const(2))))]))]))
Module('This is an example module.\n\nThis is the docstring.\n',
       Stmt([Function(None, 'double', ['x'], [], 0,
                      'Return twice the argument',
                      Stmt([Return(Mul((Name('x'), Const(2))))]))]))
>>> mod.doc
'This is an example module.\n\nThis is the docstring.\n'
>>> for node in mod.node.nodes:
...     print node
...
Function(None, 'double', ['x'], [], 0, 'Return twice the argument',
         Stmt([Return(Mul((Name('x'), Const(2))))]))
>>> func = mod.node.nodes[0]
>>> func.code
Stmt([Return(Mul((Name('x'), Const(2))))])
```

## 33.4. Использование посетителей для обхода AST

Шаблон «Посетитель» – это … Пакет [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) использует вариант шаблона «Посетитель», который использует возможности интроспекции Python для устранения необходимости в большей части инфраструктуры посетителя.

Посещаемые классы не нужно программировать на приём посетителей. Посетителю достаточно определить методы visit только для тех классов, которые его интересуют; метод visit по умолчанию обработает остальные.

XXX Магический метод `visit()` для посетителей.

#### `compiler.visitor.walk(tree, visitor[, verbose])`

#### `class compiler.visitor.ASTVisitor`

[`ASTVisitor`](https://python-all.ru/2.7/library/compiler.html#compiler.visitor.ASTVisitor) отвечает за обход дерева в правильном порядке. Обход начинается с вызова [`preorder()`](https://python-all.ru/2.7/library/compiler.html#compiler.visitor.ASTVisitor.preorder). Для каждого узла он проверяет аргумент *посетитель* в [`preorder()`](https://python-all.ru/2.7/library/compiler.html#compiler.visitor.ASTVisitor.preorder) на наличие метода с именем ‘visitNodeType,’ где NodeType – это имя класса узла, например для узла `While` будет вызван метод `visitWhile()`. Если метод существует, он вызывается с узлом в качестве первого аргумента.

Метод посетителя для конкретного типа узла может управлять тем, как дочерние узлы посещаются во время обхода. [`ASTVisitor`](https://python-all.ru/2.7/library/compiler.html#compiler.visitor.ASTVisitor) изменяет аргумент посетителя, добавляя к нему метод visit; этот метод можно использовать для посещения конкретного дочернего узла. Если для определённого типа узла посетитель не найден, вызывается метод [`default()`](https://python-all.ru/2.7/library/compiler.html#compiler.visitor.ASTVisitor.default).

Объекты [`ASTVisitor`](https://python-all.ru/2.7/library/compiler.html#compiler.visitor.ASTVisitor) имеют следующие методы:

XXX описать дополнительные аргументы

#### `default(node[, ...])`

#### `dispatch(node[, ...])`

#### `preorder(tree, visitor)`

## 33.5. Генерация байт-кода

Генератор кода – это посетитель, который генерирует байт-коды. Каждый метод visit может вызывать метод `emit()` для генерации нового байт-кода. Базовый генератор кода специализирован для модулей, классов и функций. Ассемблер преобразует сгенерированные инструкции в низкоуровневый формат байт-кода. Он обрабатывает такие задачи, как создание списков констант объектов кода и вычисление смещений переходов.
