Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как Python исполняет код
Пython использует интерпретатор для выполнения кода. Процесс включает несколько этапов: парсинг, компиляцию в байт-код, и исполнение на виртуальной машине.
Этап 1: Парсинг (Parsing)
Сначала Python парсит исходный код и проверяет синтаксис:
import ast
import inspect
# Исходный код
code = """
def hello():
print("Hello")
"""
# Парсим в AST (Abstract Syntax Tree)
tree = ast.parse(code)
print(ast.dump(tree))
# Проверка синтаксиса
try:
ast.parse("def foo(")
except SyntaxError as e:
print(f"Синтаксическая ошибка: {e}")
Этап 2: Компиляция в байт-код (Bytecode Compilation)
После парсинга код компилируется в байт-код — промежуточный формат, который исполняет виртуальная машина Python (CPython):
import dis
def add(a, b):
return a + b
# Посмотреть байт-код функции
dis.dis(add)
# Вывод:
# 2 0 LOAD_FAST 0 (a)
# 2 LOAD_FAST 1 (b)
# 4 BINARY_ADD
# 6 RETURN_VALUE
# Компилируем строку в код
code_obj = compile("x = 2 + 3", "<string>", "exec")
print(code_obj)
print(code_obj.co_code) # Байт-код в бинарном виде
# Инспектируем скомпилированный объект
print(code_obj.co_names) # Переменные
print(code_obj.co_consts) # Константы
print(code_obj.co_varnames) # Локальные переменные
Этаг 3: Исполнение на виртуальной машине (Python VM)
Виртуальная машина Python исполняет байт-код пошагово. Она использует стек для временного хранения значений:
# Пример: x = 2 + 3
# Байт-код:
# 1. LOAD_CONST 1 (2) # Загрузить 2 на стек
# 2. LOAD_CONST 2 (3) # Загрузить 3 на стек
# 3. BINARY_ADD # Сложить два верхних значения
# 4. STORE_NAME 'x' # Сохранить результат в переменную x
import sys
def show_execution():
x = 2 + 3
return x
# Показать весь байт-код функции
dis.dis(show_execution)
# Получить информацию о коде
code = show_execution.__code__
print(f"Количество аргументов: {code.co_argcount}")
print(f"Локальные переменные: {code.co_varnames}")
print(f"Константы: {code.co_consts}")
Полный процесс: парсинг → компиляция → исполнение
import marshal
import types
# Исходный Python код
source_code = """
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
result = fibonacci(5)
"""
# Шаг 1: Парсинг
ast_tree = ast.parse(source_code)
print(f"AST построено успешно")
# Шаг 2: Компиляция в байт-код
code_obj = compile(source_code, filename="<string>", mode="exec")
print(f"Байт-код скомпилирован")
print(f"Размер байт-кода: {len(code_obj.co_code)} байт")
# Шаг 3: Исполнение
namespace = {}
exec(code_obj, namespace)
print(f"Результат: {namespace['result']}")
# Можно сохранить байт-код в файл (.pyc)
with open("script.pyc", "wb") as f:
f.write(b'\\x3\\r\\r\\n') # Магический номер Python
f.write(marshal.dumps(code_obj))
Оптимизация: кэширование байт-кода
Пython автоматически кэширует скомпилированный байт-код в директорию __pycache__/:
my_project/
├── main.py
└── __pycache__/
└── main.cpython-310.pyc # Кэшированный байт-код
В следующий раз при импорте модуля Python загружает .pyc файл вместо переcompilation:
import sys
import importlib
# Посмотреть, откуда загружен модуль
import os
print(os.__file__) # Путь к модулю
# Проверить кэш импортов
print(sys.modules.keys()) # Все загруженные модули
# Принудительная перезагрузка модуля
importlib.reload(os)
Производительность байт-кода
import timeit
import dis
# Сравнение разных подходов
def using_list():
return [x*2 for x in range(1000)]
def using_loop():
result = []
for x in range(1000):
result.append(x*2)
return result
def using_map():
return list(map(lambda x: x*2, range(1000)))
print("List comprehension:")
dis.dis(using_list)
print(f"Время: {timeit.timeit(using_list, number=100000)}")
print("\\nИспользование loop:")
dis.dis(using_loop)
print(f"Время: {timeit.timeit(using_loop, number=100000)}")
print("\\nИспользование map:")
dis.dis(using_map)
print(f"Время: {timeit.timeit(using_map, number=100000)}")
Отличие от других языков
# Python (интерпретируемый язык)
# Исходный код → Парсинг → Компиляция (байт-код) → Исполнение
# Происходит каждый раз при запуске (кроме кэша)
# Java (полукомпилируемый язык)
# Исходный код → Компиляция (в .class) → JVM исполнение
# Компиляция происходит один раз
# C (компилируемый язык)
# Исходный код → Компиляция (в машинный код) → Прямое исполнение
# Самый быстрый, но нет гибкости интерпретатора
# Python преимущества:
# - Динамическая типизация
# - Быстрая разработка
# - Кроссплатформенность
# - Интерактивное выполнение (REPL)
# Python недостатки:
# - Медленнее скомпилированных языков
# - Требует Python при запуске
Практический пример: профилирование выполнения
import cProfile
import pstats
from io import StringIO
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Профилируем функцию
profiler = cProfile.Profile()
profiler.enable()
result = fibonacci(30)
profiler.disable()
# Вывод статистики
s = StringIO()
ps = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
ps.print_stats(10) # Top 10 функций
print(s.getvalue())
Резюме
- Парсинг: Проверка синтаксиса, построение AST
- Компиляция: Преобразование в байт-код (промежуточный формат)
- Исполнение: Виртуальная машина исполняет байт-код на стеке
- Кэширование: Python сохраняет .pyc файлы для быстрой загрузки
- Динамичность: Код может быть скомпилирован и выполнен во время runtime
- Гибкость: Можно использовать eval(), exec(), compile() для динамического выполнения кода