Документация Python неофициальный перевод

compiler.md

335 строк · 29.4 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/2.7/library/compiler.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 33. Пакет компилятора Python89Устарело с версии 2.6: Пакет [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) удалён в Python 3.1011Пакет компилятора Python – это инструмент для анализа исходного кода Python и генерации байт-кода Python. Компилятор содержит библиотеки для построения абстрактного синтаксического дерева из исходного кода Python и для генерации Python [байт-кода](https://python-all.ru/2.7/glossary.html#term-bytecode) из дерева.1213Пакет [`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.1415Полная функциональность пакета дублирует встроенный компилятор, поставляемый с интерпретатором Python. Он предназначен для почти точного совпадения с его поведением. Зачем реализовывать ещё один компилятор, который делает то же самое? Пакет полезен для различных целей. Его можно модифицировать проще, чем встроенный компилятор. Сгенерированное им AST полезно для анализа исходного кода Python.1617В этой главе объясняется, как работают различные компоненты пакета [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler). В ней сочетаются справочный материал и учебное пособие.1819## 33.1. Базовый интерфейс2021Верхний уровень пакета определяет четыре функции. При импорте [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) будут доступны эти функции и набор модулей, содержащихся в пакете.2223#### `compiler.parse(buf)`2425Возвращает абстрактное синтаксическое дерево для исходного кода Python в *buf*. Функция возбуждает [`SyntaxError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.SyntaxError), если в исходном коде есть ошибка. Возвращаемое значение – экземпляр `compiler.ast.Module`, содержащий дерево.2627#### `compiler.parseFile(path)`2829Возвращает абстрактное синтаксическое дерево для исходного кода Python в файле, указанном параметром *path*. Эквивалентно `parse(open(path).read())`.3031#### `compiler.walk(ast, visitor[, verbose])`3233Выполняет прямой обход абстрактного синтаксического дерева *ast*. Вызывает соответствующий метод экземпляра *visitor* для каждого встреченного узла.3435#### `compiler.compile(source, filename, mode, flags=None, dont_inherit=None)`3637Компилирует строку *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).3839Параметр *filename* будет использоваться для сообщений об ошибках времени выполнения.4041Параметр *mode* должен быть равен ‘exec’ для компиляции модуля, ‘single’ для компиляции одной (интерактивной) инструкции или ‘eval’ для компиляции выражения.4243Аргументы *flags* и *dont\_inherit* влияют на операторы, связанные с future, но пока не поддерживаются.4445#### `compiler.compileFile(source)`4647Компилирует файл *source* и генерирует файл .pyc.4849Пакет [`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`.5051## 33.2. Ограничения5253В проверке ошибок пакета компилятора есть некоторые проблемы. Интерпретатор обнаруживает синтаксические ошибки в два отдельных этапа. Один набор ошибок обнаруживается парсером интерпретатора, другой – компилятором. Пакет компилятора полагается на парсер интерпретатора, поэтому первый этап проверки ошибок он получает «бесплатно». Второй этап он реализует самостоятельно, и эта реализация неполна. Например, пакет компилятора не возбуждает ошибку, если имя встречается более одного раза в списке аргументов: `def f(x, x): ...`5455Будущая версия компилятора должна исправить эти проблемы.5657## 33.3. Абстрактный синтаксис Python5859Модуль [`compiler.ast`](https://python-all.ru/2.7/library/compiler.html#module-compiler.ast) определяет абстрактный синтаксис для Python. В абстрактном синтаксическом дереве каждый узел представляет синтаксическую конструкцию. Корнем дерева является объект `Module`.6061Абстрактный синтаксис предоставляет интерфейс более высокого уровня для разобранного исходного кода Python. Модуль [`parser`](https://python-all.ru/2.7/library/parser.html#module-parser) и компилятор, написанный на C для интерпретатора Python, используют конкретное синтаксическое дерево. Конкретный синтаксис тесно связан с описанием грамматики, используемым парсером Python. Вместо одного узла для конструкции часто существует несколько уровней вложенных узлов, которые вводятся правилами приоритета Python.6263Абстрактное синтаксическое дерево создаётся модулем `compiler.transformer`. Трансформер полагается на встроенный парсер Python для генерации конкретного синтаксического дерева. Из конкретного дерева он генерирует абстрактное синтаксическое дерево.6465Модуль `transformer` был создан Грегом Стейном и Биллом Таттом для экспериментального компилятора из Python в C. Текущая версия содержит ряд модификаций и улучшений, но основная форма абстрактного синтаксиса и трансформера принадлежит Стейну и Татту.6667### 33.3.1. Узлы AST6869Модуль [`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) и определяющим набор именованных атрибутов для дочерних узлов.7071#### `class compiler.ast.Node`7273Экземпляры [`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` привязан к одному узлу.7475Каждый экземпляр [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) имеет атрибут `lineno`, который может быть `None`. XXX Непонятно, по каким правилам узлы будут содержать полезный lineno.7677Все объекты [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) имеют следующие методы:7879#### `getChildren()`8081Возвращает плоский список дочерних узлов и объектов в порядке их появления. А именно, порядок узлов соответствует порядку, в котором они встречаются в грамматике Python. Не все дочерние элементы являются экземплярами [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node). Например, имена функций и классов – обычные строки.8283#### `getChildNodes()`8485Возвращает плоский список дочерних узлов в порядке их появления. Этот метод подобен [`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).8687Два примера иллюстрируют общую структуру классов [`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) определяется следующим грамматическим правилом:8889```python90while_stmt:     "while" expression ":" suite91               ["else" ":" suite]92```9394Узел `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).)9596Оператор [`if`](https://python-all.ru/2.7/reference/compound_stmts.html#if) более сложен, поскольку может включать несколько проверок.9798```python99if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]100```101102Узел `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`, содержащий код, который выполняется, если проверка истинна.103104Метод `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`, второе проверочное выражение и так далее.105106В следующей таблице перечислены все подклассы [`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()`.107108| Тип узла | Атрибут | Значение |109| --- | --- | --- |110| `Add` | `left` | левый операнд |111|  | `right` | правый операнд |112| `And` | `nodes` | список операндов |113| `AssAttr` |  | *атрибут как цель присваивания* |114|  | `expr` | выражение слева от точки |115|  | `attrname` | имя атрибута, строка |116|  | `flags` | XXX |117| `AssList` | `nodes` | список элементов списка, которым присваивается значение |118| `AssName` | `name` | имя, которому присваивается значение |119|  | `flags` | XXX |120| `AssTuple` | `nodes` | список элементов кортежа, которым присваивается значение |121| `Assert` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) | проверяемое выражение |122|  | `fail` | значение [`AssertionError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AssertionError) |123| `Assign` | `nodes` | список целей присваивания, по одной на знак равенства |124|  | `expr` | присваиваемое значение |125| `AugAssign` | `node` |  |126|  | `op` |  |127|  | `expr` |  |128| `Backquote` | `expr` |  |129| `Bitand` | `nodes` |  |130| `Bitor` | `nodes` |  |131| `Bitxor` | `nodes` |  |132| `Break` |  |  |133| `CallFunc` | `node` | выражение для вызываемого объекта |134|  | `args` | список аргументов |135|  | `star_args` | значение расширенного \*-аргумента |136|  | `dstar_args` | значение расширенного \*\*-аргумента |137| `Class` | `name` | имя класса, строка |138|  | `bases` | список базовых классов |139|  | `doc` | строка документации, строка или `None` |140|  | [`code`](https://python-all.ru/2.7/library/code.html#module-code) | тело инструкции класса |141| `Compare` | `expr` |  |142|  | `ops` |  |143| `Const` | `value` |  |144| `Continue` |  |  |145| `Decorators` | `nodes` | Список выражений декораторов функции |146| `Dict` | `items` |  |147| `Discard` | `expr` |  |148| `Div` | `left` |  |149|  | `right` |  |150| [`Ellipsis`](https://python-all.ru/2.7/library/constants.html#Ellipsis) |  |  |151| `Expression` | `node` |  |152| `Exec` | `expr` |  |153|  | [`locals`](https://python-all.ru/2.7/library/functions.html#locals) |  |154|  | [`globals`](https://python-all.ru/2.7/library/functions.html#globals) |  |155| `FloorDiv` | `left` |  |156|  | `right` |  |157| `For` | `assign` |  |158|  | `list` |  |159|  | `body` |  |160|  | `else_` |  |161| `From` | `modname` |  |162|  | `names` |  |163| `Function` | `decorators` | `Decorators` или `None` |164|  | `name` | имя, используемое в def, строка |165|  | `argnames` | список имён аргументов в виде строк |166|  | `defaults` | список значений по умолчанию |167|  | `flags` | xxx |168|  | `doc` | строка документации, строка или `None` |169|  | [`code`](https://python-all.ru/2.7/library/code.html#module-code) | тело функции |170| `GenExpr` | [`code`](https://python-all.ru/2.7/library/code.html#module-code) |  |171| `GenExprFor` | `assign` |  |172|  | [`iter`](https://python-all.ru/2.7/library/functions.html#iter) |  |173|  | `ifs` |  |174| `GenExprIf` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) |  |175| `GenExprInner` | `expr` |  |176|  | `quals` |  |177| `Getattr` | `expr` |  |178|  | `attrname` |  |179| `Global` | `names` |  |180| `If` | `tests` |  |181|  | `else_` |  |182| `Import` | `names` |  |183| `Invert` | `expr` |  |184| `Keyword` | `name` |  |185|  | `expr` |  |186| `Lambda` | `argnames` |  |187|  | `defaults` |  |188|  | `flags` |  |189|  | [`code`](https://python-all.ru/2.7/library/code.html#module-code) |  |190| `LeftShift` | `left` |  |191|  | `right` |  |192| `List` | `nodes` |  |193| `ListComp` | `expr` |  |194|  | `quals` |  |195| `ListCompFor` | `assign` |  |196|  | `list` |  |197|  | `ifs` |  |198| `ListCompIf` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) |  |199| `Mod` | `left` |  |200|  | `right` |  |201| `Module` | `doc` | строка документации, строка или `None` |202|  | `node` | тело модуля, `Stmt` |203| `Mul` | `left` |  |204|  | `right` |  |205| `Name` | `name` |  |206| `Not` | `expr` |  |207| `Or` | `nodes` |  |208| `Pass` |  |  |209| `Power` | `left` |  |210|  | `right` |  |211| `Print` | `nodes` |  |212|  | `dest` |  |213| `Printnl` | `nodes` |  |214|  | `dest` |  |215| `Raise` | `expr1` |  |216|  | `expr2` |  |217|  | `expr3` |  |218| `Return` | `value` |  |219| `RightShift` | `left` |  |220|  | `right` |  |221| `Slice` | `expr` |  |222|  | `flags` |  |223|  | `lower` |  |224|  | `upper` |  |225| `Sliceobj` | `nodes` | список инструкций |226| `Stmt` | `nodes` |  |227| `Sub` | `left` |  |228|  | `right` |  |229| `Subscript` | `expr` |  |230|  | `flags` |  |231|  | `subs` |  |232| `TryExcept` | `body` |  |233|  | `handlers` |  |234|  | `else_` |  |235| `TryFinally` | `body` |  |236|  | `final` |  |237| `Tuple` | `nodes` |  |238| `UnaryAdd` | `expr` |  |239| `UnarySub` | `expr` |  |240| `While` | [`test`](https://python-all.ru/2.7/library/test.html#module-test) |  |241|  | `body` |  |242|  | `else_` |  |243| `With` | `expr` |  |244|  | [`vars`](https://python-all.ru/2.7/library/functions.html#vars) |  |245|  | `body` |  |246| `Yield` | `value` |  |247248### 33.3.2. Узлы присваивания249250Существует набор узлов, используемых для представления присваиваний. Каждая инструкция присваивания в исходном коде становится одним узлом `Assign` в AST. Атрибут `nodes` представляет собой список, содержащий по одному узлу для каждой цели присваивания. Это необходимо, поскольку присваивание может быть цепочкой, например `a = b = 2`. Каждый [`Node`](https://python-all.ru/2.7/library/compiler.html#compiler.ast.Node) в списке будет одним из следующих классов: `AssAttr`, `AssList`, `AssName` или `AssTuple`.251252Каждый узел цели присваивания будет описывать тип объекта, которому выполняется присваивание: `AssName` для простого имени, например `a = 1`. `AssAttr` для присваивания атрибута, например `a.x = 1`. `AssList` и `AssTuple` для расширения списка и кортежа соответственно, например `a, b, c = a_tuple`.253254Узлы целей присваивания также имеют атрибут `flags`, который указывает, используется ли узел для присваивания или в инструкции удаления. `AssName` также используется для представления инструкции удаления, например `del x`.255256Когда выражение содержит несколько ссылок на атрибуты, инструкция присваивания или удаления будет содержать только один узел `AssAttr` – для последней ссылки на атрибут. Остальные ссылки на атрибуты будут представлены как узлы `Getattr` в атрибуте `expr` экземпляра `AssAttr`.257258### 33.3.3. Примеры259260В этом разделе приведено несколько простых примеров AST для исходного кода Python. Примеры демонстрируют, как использовать функцию `parse()`, как выглядит repr AST и как получать доступ к атрибутам узла AST.261262Первый модуль определяет одну функцию. Предположим, он сохранён в `doublelib.py`.263264```python265"""Это пример модуля.266267Это докстринг.268"""269270def double(x):271    "Return twice the argument"272    return x * 2273```274275В приведённом ниже сеансе интерактивного интерпретатора я переформатировал длинные представления AST для удобочитаемости. Представления AST используют неполные имена классов. Если вы хотите создать экземпляр на основе представления, необходимо импортировать имена классов из модуля [`compiler.ast`](https://python-all.ru/2.7/library/compiler.html#module-compiler.ast).276277```python278>>> import compiler279>>> mod = compiler.parseFile("doublelib.py")280>>> mod281Module('This is an example module.\n\nThis is the docstring.\n',282       Stmt([Function(None, 'double', ['x'], [], 0,283                      'Return twice the argument',284                      Stmt([Return(Mul((Name('x'), Const(2))))]))]))285>>> from compiler.ast import *286>>> Module('This is an example module.\n\nThis is the docstring.\n',287...    Stmt([Function(None, 'double', ['x'], [], 0,288...                   'Return twice the argument',289...                   Stmt([Return(Mul((Name('x'), Const(2))))]))]))290Module('This is an example module.\n\nThis is the docstring.\n',291       Stmt([Function(None, 'double', ['x'], [], 0,292                      'Return twice the argument',293                      Stmt([Return(Mul((Name('x'), Const(2))))]))]))294>>> mod.doc295'This is an example module.\n\nThis is the docstring.\n'296>>> for node in mod.node.nodes:297...     print node298...299Function(None, 'double', ['x'], [], 0, 'Return twice the argument',300         Stmt([Return(Mul((Name('x'), Const(2))))]))301>>> func = mod.node.nodes[0]302>>> func.code303Stmt([Return(Mul((Name('x'), Const(2))))])304```305306## 33.4. Использование посетителей для обхода AST307308Шаблон «Посетитель» – это … Пакет [`compiler`](https://python-all.ru/2.7/library/compiler.html#module-compiler) использует вариант шаблона «Посетитель», который использует возможности интроспекции Python для устранения необходимости в большей части инфраструктуры посетителя.309310Посещаемые классы не нужно программировать на приём посетителей. Посетителю достаточно определить методы visit только для тех классов, которые его интересуют; метод visit по умолчанию обработает остальные.311312XXX Магический метод `visit()` для посетителей.313314#### `compiler.visitor.walk(tree, visitor[, verbose])`315316#### `class compiler.visitor.ASTVisitor`317318[`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()`. Если метод существует, он вызывается с узлом в качестве первого аргумента.319320Метод посетителя для конкретного типа узла может управлять тем, как дочерние узлы посещаются во время обхода. [`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).321322Объекты [`ASTVisitor`](https://python-all.ru/2.7/library/compiler.html#compiler.visitor.ASTVisitor) имеют следующие методы:323324XXX описать дополнительные аргументы325326#### `default(node[, ...])`327328#### `dispatch(node[, ...])`329330#### `preorder(tree, visitor)`331332## 33.5. Генерация байт-кода333334Генератор кода – это посетитель, который генерирует байт-коды. Каждый метод visit может вызывать метод `emit()` для генерации нового байт-кода. Базовый генератор кода специализирован для модулей, классов и функций. Ассемблер преобразует сгенерированные инструкции в низкоуровневый формат байт-кода. Он обрабатывает такие задачи, как создание списков констант объектов кода и вычисление смещений переходов.335