Как Python исполняет файл
Что проверяют
Знаете ли вы, что происходит между командой python app.py и первой строкой вывода, и можете ли развеять миф «Python исполняет исходник построчно». Следом обычно спрашивают, когда появляются файлы .pyc и почему код модуля выполняется при импорте.
Как отвечать
Речь о CPython, эталонной реализации. Путь файла такой:
- чтение: исходник читается как байты и декодируется, по умолчанию из UTF-8;
- разбор: токенизатор, затем PEG-парсер (с версии 3.9) строит абстрактное синтаксическое дерево, его можно посмотреть модулем
ast. Синтаксическая ошибка всплывает здесь, до выполнения первой строки; - компиляция: по AST строится таблица символов (что локальное, что глобальное, что замыкание) и объекты кода с байт-кодом. Посмотреть байт-код можно через
dis; - выполнение: стековая виртуальная машина в цикле исполняет инструкции байт-кода одну за другой. С 3.11 интерпретатор адаптивный: горячие инструкции подменяются специализированными под конкретные типы;
- память: подсчёт ссылок плюс циклический сборщик мусора из модуля
gc.
Про .pyc: кэш пишется для импортируемых модулей, в __pycache__/имя.cpython-313.pyc. Главный скрипт, который вы запустили, не кэшируется и компилируется заново при каждом запуске. При импорте CPython сверяет время изменения и размер исходника и перекомпилирует модуль, если тот изменился. Запись кэша отключает флаг -B.
Импорт выполняет весь код верхнего уровня модуля один раз и кладёт модуль в sys.modules. Отсюда смысл if __name__ == "__main__":: у запущенного скрипта имя __main__, у импортированного модуля его собственное.
Где спотыкаются
Говорят, что Python не компилируется. Компилируется, только в байт-код, а не в машинный код.
Уверены, что .pyc ускоряет выполнение. Он экономит только время на разбор и компиляцию при старте, сам байт-код работает с той же скоростью.
Держат в модуле код верхнего уровня с побочными эффектами: подключение к базе, запуск сервера. Всё это случится при первом же импорте, включая импорт из тестов.