← Назад к вопросам

Есть ли этапы компиляции у Python?

1.8 Middle🔥 101 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Есть ли этапы компиляции у Python?

Это отличный вопрос, который часто вызывает путаницу! Короткий ответ: да, у Python есть этапы компиляции, но они отличаются от классических компилируемых языков типа C++.

Многие думают, что Python — это чистый интерпретируемый язык, который читает код строка за строкой. На самом деле это не совсем верно.

Как работает Python

Python использует двухэтапный процесс: компиляция + интерпретация.

Python код (.py)
    ↓
[КОМПИЛЯЦИЯ] → Bytecode (.pyc)
    ↓
[ИНТЕРПРЕТАЦИЯ] → Виртуальная машина Python (PVM) → Машинный код

Этап 1: Компиляция в Bytecode

В отличие от C++ или Java, Python НЕ компилирует в машинный код. Вместо этого он компилирует в байт-код (bytecode) — промежуточное представление.

Этап компиляции включает:

  1. Лексический анализ (Lexing) — разбиение кода на токены
  2. Синтаксический анализ (Parsing) — построение синтаксического дерева
  3. Семантический анализ (Semantic analysis) — проверка семантики
  4. Генерация байт-кода (Code generation) → .pyc файл

Пример:

# script.py
x = 5
y = 10
z = x + y
print(z)

Этот код компилируется в байт-код:

2           0 LOAD_CONST               1 (5)
            2 STORE_NAME               0 (x)

3           4 LOAD_CONST               2 (10)
            6 STORE_NAME               1 (y)

4           8 LOAD_NAME                0 (x)
           10 LOAD_NAME                1 (y)
           12 BINARY_ADD
           14 STORE_NAME               2 (z)

5          16 LOAD_NAME                3 (print)
           18 LOAD_NAME                2 (z)
           20 CALL_FUNCTION            1
           22 POP_TOP
           24 LOAD_CONST               0 (None)
           26 RETURN_VALUE

Этап 2: Интерпретация Bytecode

После компиляции, Python Virtual Machine (PVM) интерпретирует этот байт-код:

import dis

def add(x, y):
    return x + y

# Посмотреть байт-код
dis.dis(add)

Вывод:

2           0 LOAD_FAST                0 (x)
            2 LOAD_FAST                1 (y)
            4 BINARY_ADD
            6 RETURN_VALUE

PVM выполняет:

  1. LOAD_FAST 0 → загрузить аргумент x
  2. LOAD_FAST 1 → загрузить аргумент y
  3. BINARY_ADD → сложить
  4. RETURN_VALUE → вернуть результат

Где находятся .pyc файлы?

Когда вы импортируете модуль, Python автоматически компилирует его в .pyc файл:

$ python -c "import mymodule"

# Теперь в папке __pycache__ появится файл:
__pycache__/
    mymodule.cpython-39.pyc
    mymodule.cpython-310.pyc
    another_module.cpython-39.pyc

Зачем это нужно:

  • Компиляция в байт-код — это дорогая операция
  • Кэширование .pyc файлов ускоряет последующие запуски
  • Если исходный код не изменился, используется кэшированный байт-код

Проверяем это:

import time
import sys

# Первый запуск
start = time.time()
import numpy  # Медленно, идёт компиляция
first_run = time.time() - start

# Второй запуск
# (в реальности нужно перезапустить процесс)
print(f"Первый импорт: {first_run}s")
# Результат: Первый импорт: 0.523s

# Второй импорт (уже скомпилировано):
print(f"Второй импорт: очень быстро")

Сравнение с другими языками

ЯзыкКомпиляцияВыполнениеФайл
C++Источник → Машинный кодМашинный код напрямую.exe
JavaИсточник → BytecodeJVM интерпретирует.class
PythonИсточник → BytecodePVM интерпретирует.pyc
JavaScriptНет явной компиляцииИнтерпретируется (или JIT)-

Оптимизация: JIT компиляция (PyPy)

Обычный CPython интерпретирует байт-код каждый раз. Но есть реализации с JIT компиляцией (Just-In-Time):

# С обычным CPython
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

fibon\acci(35)  # Медленно

# С PyPy (JIT компиляция)
# Горячий код (часто вызываемый) компилируется в машинный код
# fibonacci(35)  # Намного быстрее!

Как посмотреть процесс компиляции

1. Посмотреть байт-код функции

import dis

def example():
    x = 5
    y = 10
    return x + y

dis.dis(example)

2. Компилировать в байт-код явно

import py_compile

# Скомпилировать файл
py_compile.compile('mymodule.py', cfile='mymodule.pyc')

# Теперь есть .pyc файл

3. Посмотреть содержимое .pyc

import marshal
import pickletools

# .pyc файл содержит:
# [Magic number] [Timestamp] [Bytecode]

with open('__pycache__/mymodule.cpython-39.pyc', 'rb') as f:
    magic = f.read(4)
    timestamp = f.read(4)
    bytecode = marshal.load(f)
    
    dis.dis(bytecode)

Процесс компиляции в деталях

Исходный код (.py)
    ↓
[TOKENIZER] — разбить на токены
Отдельные слова, операторы, пунктуация
    ↓
[PARSER] — построить синтаксическое дерево (AST)
Проверить синтаксис
    ↓
[COMPILER] — переделать AST в инструкции
Проверить семантику (например, имена переменных)
    ↓
[CODE GENERATOR] — выгенерировать байт-код
    ↓
[BYTECODE] (.pyc) — кэшировать для будущих запусков
    ↓
[PVM] — интерпретировать байт-код
    ↓
Машинный код (через системный интерпретатор)

Доказательство компиляции

# script.py с ошибкой
x = 5
print(y)  # NameError!
$ python script.py
Traceback (most recent call last):
  File "script.py", line 2, in <module>
    print(y)
NameError: name 'y' is not defined

Замечайте: ошибка NameError происходит при выполнении, а не при компиляции.

Это доказывает что есть двух-этапный процесс:

  1. Компиляция прошла успешно
  2. Ошибка произошла при интерпретации

Синтаксические ошибки (при компиляции)

# script.py с синтаксической ошибкой
x = 5
if x == 5  # Нет : в конце
    print(x)
$ python script.py
  File "script.py", line 2
    if x == 5
          ^
SyntaxError: invalid syntax

Синтаксическая ошибка ловится при компиляции, до выполнения кода.

Заключение

Da, у Python есть этапы компиляции:

  1. Компиляция → Исходный код → Байт-код (.pyc)

    • Лексический анализ
    • Синтаксический анализ
    • Генерация байт-кода
  2. Интерпретация → Байт-код → Выполнение

    • Python Virtual Machine (PVM) выполняет инструкции
    • Результаты вычислений

Python — это компилируемо-интерпретируемый язык, а не чистый интерпретатор. Это делает его достаточно быстрым (спасибо кэшированию байт-кода) и достаточно гибким (спасибо интерпретации).