Короткий ответ
Python сочетает оба подхода: исходный код компилируется в байткод, а виртуальная машина затем интерпретирует его. В основной реализации CPython этапы объединены, поэтому корректный ответ — «и то и другое».
Как это работает подробнее
Исполнение программы проходит в два этапа, поэтому у Python есть черты и компилируемых, и интерпретируемых языков.
- Компиляция в байткод. Исходный код из файлов
.pyпереводится в промежуточное представление — байткод, который сохраняется в файлы.pyc. - Интерпретация байткода. Виртуальная машина Python исполняет байткод, поэтому код можно запускать построчно, а ошибки типов и часть проблем обнаруживаются уже в рантайме.
- Реализации. CPython совмещает оба режима, а PyPy дополнительно использует JIT-компиляцию для ускорения выполнения.
Что использовать на практике
- Запуск без компиляции. Отдельной команды компиляции нет: файл исполняется сразу, а байткод переиспользуется при повторных запусках.
- Кэш байткода. Файлы
.pycускоряют повторные запуски, ведь компиляция выполняется только один раз. - Ранняя диагностика. Помни, что значительная часть ошибок проявляется во время исполнения, поэтому без тестов не обойтись.
Подводные камни
- Терминология. «Интерпретируемый» не значит, что код выполняется прямо из исходников: между ними всегда есть байткод.
- Разные реализации. CPython, PyPy и другие реализации исполняют код по-своему, что влияет на производительность и поведение.
- Смешение понятий. Компиляцию в байткод часто путают с нативной компиляцией в машинный код, а это разные процессы.