Python любят за простоту и читаемость. Но иногда он тормозит. Почему это происходит и как можно ускорить код — рассказываем в статье.
Почему код работает медленно
Python — универсальный язык программирования. Его используют для веб-разработки, написания скриптов, машинного обучения. Но иногда программа может работать медленно. Например, из-за слишком сложного алгоритма, лишних операций или неправильной работы с данными.
При этом тормозит, как правило, не вся программа, а небольшой фрагмент кода. Чтобы найти узкое место, нужно измерить время выполнения операции. Для этого используют:
- Модуль time — для простого измерения скорости Python на участке кода (засекает время один раз).
- Модуль timeit — для более точных измерений небольших фрагментов кода (запускает их несколько раз и считает среднее значение).
Это поможет понять, что именно нужно оптимизировать, и исправить проблему.

Как оптимизировать код на Python
Если несколько способов, как оптимизировать код Python.
Поменять структуру данных
Часто код тормозит, потому что программа делает слишком много лишней работы. Представьте, что у вас есть список из 10 000 имен и нужно проверить, есть ли среди них конкретное имя.
Самый простой способ — перебирать список по порядку. Но если нужное имя в самом конце, то программа проверит почти 10 000 элементов. То же самое происходит с кодом. Ускорить его можно, если правильно выбрать структуру данных:
- list — хранит элементы по порядку;
- set — оптимизирован для быстрой проверки, может почти мгновенно сказать, есть нужный элемент или нет;
- dict — позволяет быстро получить значение по ключу.
Если использовать list там, где нужен set или dict, программа будет выполнять лишние проверки.
Структуры данных влияют не только на скорость поиска, но также:
- на добавление элементов;
- удаление;
- сортировку;
- объем используемой памяти.
Поэтому правильный выбор структуры часто дает больший прирост скорости, чем оптимизации внутри кода. Думайте, что именно должна делать программа (искать, добавлять, сортировать, хранить данные), а потом выбирайте структуру под задачу.

Убрать лишние операции
Иногда проблема не в алгоритме, а в способе записи кода. Если есть много мелких повторяющихся операций, они будут замедлять программу.
Представим, что нужно создать список чисел, умноженных на 2. Можно написать так:

Или так:

В первом случае Python выполняет несколько действий на каждой итерации: ищет метод append, вызывает его, добавляет элемент. А во втором механизм создания списка оптимизирован внутри интерпретатора — он требует меньше внутренних шагов и выполняется быстрее.
Использовать NumPy вместо Python
Python — интерпретируемый язык программирования. Это значит, что каждую строчку он разбирает прямо во время выполнения. И каждый раз:
- проверяет типы;
- управляет объектами;
- вызывает операции.
Допустим, у нас есть миллион чисел и мы хотим умножить каждое на 2. Python будет проходить по каждому элементу и выполнять операцию отдельно.

Или можно использовать NumPy — он написан на языке C и работает ближе к «железу». Поэтому вычисления происходят не в чистом Python, а внутри оптимизированного C-кода:

При работе с большими числами, массивами и таблицами данных почти всегда NumPy будет работать быстрее, чем Python.
Использовать параллелизм
Иногда программа тормозит, потому что делает слишком много работы последовательно. Параллелизм делит ее на части и выполняет их одновременно. В Python это может быть:
- Multiprocessing — создает несколько процессов, каждый со своим Python-интерпретатором. Они не мешают друг другу и используют несколько ядер процессора.
- Threading — запускает несколько потоков внутри одной программы, и каждый выполняет часть работы. Но из-за глобальной блокировки интерпретатора (GIL) не всегда дает прирост скорости питона.
- Asyncio — позволяет программе не ждать завершения медленной операции, а переключаться на другие задачи.
Если программа в основном вычисляет числа или обрабатывает данные, лучше всего подойдет multiprocessing. Если она ждет внешних ресурсов (сеть, файлы, базу) — подойдут threading (для небольшого количества задач) или asyncio (когда «ожиданий» много).

Кэшировать результаты
Еще один способ ускорить Python — кэширование. Его используют, если программа повторяет одни и те же действия:
- вызывает функцию с одинаковыми данными;
- использует один и тот же результат вычислений;
- работает с тяжелыми операциями, которые требуют времени (например, запросы к базе, вычисления, загрузка файлов).
Python умеет «запоминать» результаты. При этом функция выполняется только один раз для каждого уникального аргумента. Например, программа вычислила стоимость доставки для конкретного адреса — эту информацию можно сохранить, чтобы не считать заново.
Кэширование экономит время, но нужно контролировать размер кэша. Если его слишком много, он сам может замедлять программу.

Ошибки при оптимизации Python
Самые распространенные ошибки при оптимизации кода:
- Менять код на глаз: измерения нужны, чтобы найти узкое место программы. Если оптимизировать наугад, можно зря потратить время.
- Жертвовать читаемостью ради небольшого прироста скорости: буст на 2–5% на практике будет почти незаметен, но может сильно ухудшить качество кода.
- Игнорировать архитектуру: программа всегда будет работать медленно, если к базе данных идет слишком много запросов или если каждый раз данные загружаются из огромного файла.
- Не учитывать память: временные объекты, списки и массивы замедляют программу, когда операционная система использует диск вместо RAM.
Оптимизация Python всегда начинается с измерений. Если после правок код стал сложнее, а прироста скорости почти нет, скорее всего, вы оптимизировали не тот фрагмент.

Главное про оптимизацию кода Python
- Не оптимизируйте код на глаз, а измеряйте с помощью модуля time.
- Самое главное — алгоритмы и структуры данных. В некоторых задачах замена list на set или dict может дать ускорение Python в десятки раз.
- Убирайте лишние операции, они замедляют программу.
- Если вы работаете с числами, массивами или таблицами данных, используйте библиотеки на C, например NumPy или Pandas.
- Применяйте параллелизм: multiprocessing — для тяжелых вычислений, threading или asyncio — для ожидания.
- Если функция часто вызывается с одинаковыми данными, кэшируйте результаты.
- Читаемость кода важнее незначительного ускорения Python на 2–5%.
