- •Об авторе
- •О группе редакторов
- •Предисловие
- •Введение
- •Как использовать эту книгу
- •Загрузка исходного кода CPython
- •Что в исходном коде?
- •Настройка среды разработки
- •IDE или редактор?
- •Настройка Visual Studio
- •Настройка Visual Studio Code
- •Настройка Vim
- •Выводы
- •Компиляция CPython
- •Компиляция CPython на macOS
- •Компиляция CPython на Linux
- •Установка специализированной версии
- •Знакомство с Make
- •Make-цели CPython
- •Компиляция CPython на Windows
- •Профильная оптимизация
- •Выводы
- •Грамматика и язык Python
- •Спецификация языка Python
- •Генератор парсеров
- •Повторное генерирование грамматики
- •Выводы
- •Конфигурация и ввод
- •Конфигурация состояния
- •Структура данных конфигурации среды выполнения
- •Конфигурация сборки
- •Сборка модуля из входных данных
- •Выводы
- •Генерирование конкретного синтаксического дерева
- •Парсер/токенизатор CPython
- •Абстрактные синтаксические деревья
- •Важные термины
- •Пример: добавление оператора «почти равно»
- •Выводы
- •Компилятор
- •Исходные файлы
- •Важные термины
- •Создание экземпляра компилятора
- •Флаги будущей функциональности и флаги компилятора
- •Таблицы символических имен
- •Основная компиляция
- •Ассемблер
- •Создание объекта кода
- •Использование Instaviz для вывода объекта кода
- •Пример: реализация оператора «почти равно»
- •Выводы
- •Цикл вычисления
- •Исходные файлы
- •Важные термины
- •Построение состояния потока
- •Построение объектов кадров
- •Выполнение кадра
- •Стек значений
- •Пример: добавление элемента в список
- •Выводы
- •Управление памятью
- •Выделение памяти в C
- •Проектирование системы управления памятью Python
- •Аллокаторы памяти CPython
- •Область выделения объектной памяти и PyMem
- •Область выделения сырой памяти
- •Нестандартные области выделения памяти
- •Санитайзеры выделенной памяти
- •Арена памяти PyArena
- •Подсчет ссылок
- •Сборка мусора
- •Выводы
- •Параллелизм и конкурентность
- •Модели параллелизма и конкурентности
- •Структура процесса
- •Многопроцессорный параллелизм
- •Многопоточность
- •Асинхронное программирование
- •Генераторы
- •Сопрограммы
- •Асинхронные генераторы
- •Субинтерпретаторы
- •Выводы
- •Объекты и типы
- •Примеры этой главы
- •Встроенные типы
- •Типы объектов
- •Тип type
- •Типы bool и long
- •Тип строки Юникода
- •Словари
- •Выводы
- •Стандартная библиотека
- •Модули Python
- •Модули Python и C
- •Набор тестов
- •Запуск набора тестов в Windows
- •Запуск набора тестов в Linux или macOS
- •Флаги тестирования
- •Запуск конкретных тестов
- •Модули тестирования
- •Вспомогательные средства тестирования
- •Выводы
- •Отладка
- •Обработчик сбоев
- •Компиляция поддержки отладки
- •LLDB для macOS
- •Отладчик Visual Studio
- •Отладчик CLion
- •Выводы
- •Бенчмаркинг, профилирование и трассировка
- •Использование timeit для микробенчмарка
- •Использование набора тестов производительности Python
- •Профилирование кода Python с использованием cProfile
- •Выводы
- •Что дальше?
- •Создание расширений C для CPython
- •Улучшение приложений Python
- •Участие в проекте CPython
- •Дальнейшее обучение
- •Препроцессор C
- •Базовый синтаксис C
- •Выводы
- •Благодарности
Выводы 85
Входные данные из скомпилированного байт-кода
Если пользователь выполняет python с путем к файлу .pyc, то вместо того, чтобы загрузить его как простой текстовый файл и распарсить, CPython предполагает, что файл .pyc содержит программный объект, записанный на диск.
В PyRun_SimpleFileExFlags() есть условие для передачи пользователем пути к файлу .pyc.
Функция run_pyc_file() из файла Python pythonrun.c осуществляет маршалинг объектного кода из файла .pyc с использованием дескриптора файла.
Структура данных объектного кода на диске используется компилятором CPython для кэширования скомпилированного кода, чтобы его не приходилось заново парсить при каждом вызове скрипта.
ПРИМЕЧАНИЕ
Термином«маршалинг»обозначается копирование содержимого файла в память и преобразование его в конкретную структуру данных.
Когда объектный код маршализован в память, он передается функции run_ eval_code_obj(), которая вызывает Python ceval.c для выполнения.
ВЫВОДЫ
В этой главе вы узнали, как загружаются многочисленные параметры конфигурации Python и как код передается на вход интерпретатора.
Благодаря возможности выбора ввода Python отлично подходит для разно образных приложений, например:
zz утилит командной строки;
zz постоянно работающих сетевых приложений (например, веб-серверов); zz коротких скриптов компоновки.
Книги для программистов: https://t.me/booksforits
86 Конфигурация и ввод
Возможность разнообразной настройки свойств конфигурации в Python создает некоторую сложность. Например, если вы протестировали Pythonприложение в среде с Python 3.8 и оно было выполнено правильно, а в другой среде произошла ошибка, вам придется выяснить, какие настройки отличались.
Это означает, что придется проверить переменные среды, флаги среды выполнения и даже свойства системной конфигурации.
Свойства времени компиляции, находящиеся в системной конфигурации, могут различаться между дистрибутивами Python. Например, дистрибутив Python 3.8 для macOS, загруженный с Python.org, по умолчанию использует значения, отличные от тех, которые использует Python 3.8 из Homebrew или Anaconda.
При любом способе ввода на выходе вы получаете модуль Python. В следующей главе вы увидите, как это происходит.
Книги для программистов: https://t.me/booksforits
Лексический анализ и парсинг с использованием
синтаксических деревьев
В предыдущей главе вы узнали, как происходит чтение Python-кода из разных источников. Затем его необходимо преобразовать в структуру, которая может использоваться компилятором.
Эта стадия называется парсингом (parsing):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/ |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
В этой главе вы узнаете, как текст разбирается в логическую структуру, которая может быть обработана компилятором.
При парсинге кода в CPython используются две структуры: конкретное синтаксическое дерево (CST) и абстрактное синтаксическое дерево (AST):
|
|
|
CST |
|
AST |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
Книги для программистов: https://t.me/booksforits
88 Лексический анализ и парсинг с использованием синтаксических деревьев
Процесс парсинга состоит из двух частей:
1.Создание конкретного синтаксического дерева (CST, concrete syntax tree) c использованием парсера/токенизатора (parser-tokenizer) или лексического анализатора (lexer).
2.Создание абстрактного синтаксического дерева (AST, abstract syntax tree) из конкретного синтаксического дерева с использованием парсера.
Эти два шага — распространенные парадигмы, используемые во многих языках программирования.
ГЕНЕРИРОВАНИЕ КОНКРЕТНОГО СИНТАКСИЧЕСКОГО ДЕРЕВА
CST, иногда называемое деревом разбора, — упорядоченная древовидная структура с корнем, представляющая код в контекстно-свободной грамматике.
CST строится токенизатором и парсером. Парсер рассматривался в главе «Язык Python и грамматика». Вывод парсера представляет собой таблицу разбора в форме детерминированного конечного автомата (ДКА), описывающую возможные состояния контекстно-свободной грамматики.
СМ. ТАКЖЕ
Создатель Python Гвидо ван Россум разработал контекстную грамматику для использования в CPython 3.9 как альтернативу для LL(1) — граммати ки, использованной в предыдущих версиях CPython. Новая грамматика называется PEG (Parser Expression Grammar).
Парсер PEG стал доступным вPython 3.9.ВPython 3.10 старая грамматика LL(1) была полностью исключена.
В главе «Язык Python и грамматика» рассматривались некоторые типы выражений, такие как if_stmt и with_stmt. CST представляет грамматические обозначения вида if_stmt как ветви, а лексемы и терминалы — как листовые узлы.
Книги для программистов: https://t.me/booksforits
Генерирование конкретного синтаксического дерева 89
Например, арифметическое выражение a+1 преобразуется в следующее дерево CST:
|
|
arith_expr |
|
|
|
||
|
term |
|
|
|
|
|
|
|
PLUS |
|
term |
||||
|
factor |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
'+' |
|
|
factor |
|||
|
power |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
power |
|||
|
atom_expr |
|
|
|
|
|
|
|
|
|
|
atom_expr |
|||
|
atom |
|
|
|
|
|
|
|
|
|
|
atom |
|||
NAME |
'a' |
|
|
|
1 |
||
|
NUMBER |
|
Арифметическое выражение представляется тремя основными ветвями: левой ветвью, ветвью оператора и правой ветвью. Парсер перебирает лексемы из входного потока и сопоставляет их с возможными состояниями и лексемами в грамматике для построения CST.
Все символические имена в CST определяются в Grammar Grammar:
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)* factor: ('+'|'-'|'~') factor | power
power: atom_expr ['**' factor] atom_expr: [AWAIT] atom trailer*
atom: ('(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
Лексемы определяются в Grammar Tokens:
ENDMARKER
NAME
NUMBER
STRING
NEWLINE
INDENT
DEDENT
Книги для программистов: https://t.me/booksforits
90 Лексический анализ и парсинг с использованием синтаксических деревьев
LPAR |
'(' |
RPAR |
')' |
LSQB |
'[' |
RSQB |
']' |
COLON |
':' |
COMMA |
',' |
SEMI |
';' |
PLUS |
'+' |
MINUS |
'-' |
STAR |
'*' |
... |
|
Лексема NAME представляет имя переменной, функции, класса или модуля. Синтаксис Python не допускает, чтобы NAME было одним из зарезервированных ключевых слов (например, await или async), а также числовым или другим литеральным типом. Например, если вы попытаетесь определить функцию с именем 1, Python выдаст ошибку синтаксиса (SyntaxError):
>>> def 1():
File "<stdin>", line 1 def 1():
^
SyntaxError: invalid syntax
NUMBER — особая разновидность лексем для представления одного из числовых значений Python. В Python существует специальная грамматика для чисел:
zz Восьмеричные значения, например 0o20
zz Шестнадцатеричные значения, например 0x10 zz Двоичные значения, например 0b10000
zz Комплексные числа, например 10j
zz Числа с плавающей точкой, например 1.01
zz Подчеркивания в качестве разделителя разрядов, например 1_000_000
Для просмотра скомпилированных символических имен и лексем в Python можно воспользоваться модулями symbol и token:
$ ./python
>>>import symbol
>>>dir(symbol)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
Книги для программистов: https://t.me/booksforits