Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
PyC — Скомпилированный байткод Python
PyC (Python Compiled) — это формат файлов скомпилированного байткода Python с расширением .pyc. Они хранят промежуточное представление исходного кода Python, оптимизированное для выполнения виртуальной машиной CPython. PyC файлы — критический механизм для ускорения загрузки модулей и экономии ресурсов.
Как работает компиляция в PyC?
Когда Python интерпретатор импортирует модуль, он проходит несколько этапов:
- Парсинг — исходный код
.pyпреобразуется в AST (Abstract Syntax Tree) - Компиляция — AST компилируется в байткод (низкоуровневые инструкции для Python VM)
- Кэширование — байткод сохраняется в файл
.pycв директории__pycache__ - Выполнение — байткод выполняется CPython виртуальной машиной
На следующий раз при импорте сохранённый .pyc используется вместо повторной компиляции:
# mymodule.py
def calculate(x, y):
return x + y
# main.py
import mymodule # Первый импорт: парсинг + компиляция + сохранение в .pyc
mymodule.calculate(5, 3)
Структура PyC файла
PyC файл содержит заголовок и комплилированный байткод:
[Magic Number (4 байта)] [Timestamp (4 байта)] [File Size (4 байта)] [Bytecode]
- Magic Number — идентификатор версии Python (различается для разных версий: 3.9, 3.10, 3.11 и т.д.)
- Timestamp — время модификации исходного файла (для проверки кэша)
- Bytecode — скомпилированный код
Анализ PyC файлов
import dis
import marshal
# Способ 1: Дизассемблирование с помощью dis
def greet(name):
greeting = f"Hello, {name}!"
print(greeting)
dis.dis(greet)
# Способ 2: Работа с PyC файлом напрямую
with open('__pycache__/mymodule.cpython-310.pyc', 'rb') as f:
magic = f.read(4) # Magic number
flags = f.read(4) # Флаги
timestamp = f.read(4) # Timestamp
size = f.read(4) # Размер исходного файла
bytecode = marshal.load(f) # Байткод
print(f'Magic: {magic.hex()}')
print(f'Bytecode: {bytecode}')
Когда PyC переносится?
PyC пересоздаётся, если:
- Исходный файл изменился — timestamp больше не совпадает
- Версия Python изменилась — magic number не совпадает
- Удалён .pyc — будет пересоздан при следующем импорте
- Изменилась переменная окружения —
PYTHONDONTWRITEBYTECODE=1запретит создание .pyc
# Запретить создание .pyc файлов
export PYTHONDONTWRITEBYTECODE=1
# Очистить все .pyc файлы
find . -type d -name __pycache__ -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
# Компилировать модули заранее (для оптимизации)
python -m compileall mypackage/
Оптимизация с помощью PyC
# Вариант 1: Автоматическое кэширование (по умолчанию)
import sys
sys.dont_write_bytecode = False # По умолчанию
# Вариант 2: Предварительная компиляция для production
import compileall
compileall.compile_dir(
'mypackage',
force=True, # Пересоздать все .pyc
quiet=1 # Не выводить логи
)
# Вариант 3: Использование py_compile для отдельных файлов
import py_compile
py_compile.compile('mymodule.py', cfile='__pycache__/mymodule.pyc')
PyC в production
В production окружении .pyc файлы имеют большое значение:
import sys
import py_compile
import os
def prepare_for_production(package_dir):
# Компилируем весь пакет
for root, dirs, files in os.walk(package_dir):
for file in files:
if file.endswith('.py'):
filepath = os.path.join(root, file)
try:
py_compile.compile(filepath, optimize=2)
except py_compile.PyCompileError as e:
print(f'Failed to compile {filepath}: {e}')
# Проверяем наличие .pyc
pyc_count = sum(
1 for root, dirs, files in os.walk(package_dir)
for f in files if f.endswith('.pyc')
)
print(f'Compiled {pyc_count} modules')
prepare_for_production('myapp')
Уровни оптимизации PyC
# -O: базовая оптимизация (удаление assert, __debug__ блоков)
python -O myapp.py
# -OO: агрессивная оптимизация (удаление docstrings)
python -OO myapp.py
# Уровни сохраняются в расширении .pyc:
# mymodule.cpython-310.pyc (без оптимизации)
# mymodule.cpython-310.opt-1.pyc (с -O)
# mymodule.cpython-310.opt-2.pyc (с -OO)
Проблемы с PyC
Проблема 1: Устаревший кэш
Если забыть очистить .pyc, старый код может выполняться. Всегда очищай кэш при обновлениях:
rm -rf __pycache__/
Проблема 2: Безопасность
.pyc файлы можно декомпилировать (uncompyle6, decompyle3). Не полагайся на .pyc как на защиту от обратного инжиниринга:
pip install uncompyle6
uncompyle6 __pycache__/mymodule.cpython-310.pyc > recovered.py
Проблема 3: Кроссплатформные различия
.pyc привязаны к версии Python, но не к операционной системе. Один .pyc может использоваться на Linux, Windows, macOS (если одна версия Python).
Проверка наличия PyC
import importlib.util
def is_cached(module_name):
spec = importlib.util.find_spec(module_name)
if spec and spec.cached:
return True
return False
print(is_cached('json')) # True — встроенный модуль уже скомпилирован
print(is_cached('mymodule')) # True/False в зависимости от кэша
Резюме: PyC файлы — это кэш скомпилированного байткода, который ускоряет загрузку модулей в Python. Хотя они создаются автоматически, понимание их природы помогает оптимизировать приложения и диагностировать проблемы с загрузкой модулей.