| Ошибка | Почему медленно | Быстрое решение |
|---|---|---|
Поиск в списке (if x in my_list) |
Сканирует элементы последовательно → O(n). При миллионах элементов каждый запрос - полный проход. | Заменить список на set или dict - поиск становится O(1). |
| N + 1 запросов | Один запрос получает список, а затем для каждого элемента делается отдельный запрос к БД/API. В итоге сотни‑тысячи небольших запросов. | Сгруппировать запросы: использовать JOIN, select_related/prefetch_related (Django) или собрать данные одним вызовом API. |
Конкатенация строк в цикле (s += "text") |
Строки неизменяемы, каждый += создаёт новый объект и копирует старый + новый кусок. Память и процессор тратятся зря. |
Накапливать части в list, а в конце собрать ''.join(parts). |
Чтение целого файла в память (f.read()) |
При больших файлах (гигабайты) сразу занимает всю RAM → MemoryError. |
Читать построчно: for line in f: или использовать генераторы/readline(). |
| Неэффективные вложенные циклы | Два‑три уровня вложения дают квадратичную сложность O(n²). | Предварительно построить lookup‑dict/set для внутреннего набора и обращаться к нему напрямую. |
Открытие/закрытие ресурсов в цикле (open() внутри for) |
Каждый проход делает системный вызов - дорогой handshake. | Открыть ресурс один раз внешне (контекстный менеджер with open(...) as f:) и использовать внутри цикла. |
Игнорирование встроенных функций (for i in data: ... вместо sum(), sorted()) |
Интерпретатор каждый раз декодирует цикл, проверяет типы и т.д. | Пользоваться stdlib: sum(), sorted(), filter(), map(), itertools - они написаны на C и работают быстрее. |
Как быстро проверить свой код
import timeit
def slow():
s = ''
for i in range(100_000):
s += str(i) # плохой вариант
return s
def fast():
parts = [str(i) for i in range(100_000)]
return ''.join(parts) # хороший вариант
print(timeit.timeit(slow, number=1))
print(timeit.timeit(fast, number=1))
Разница обычно в десятки раз.
Большинство "тормозов" - это не магия алгоритмов, а простые привычки: использовать неподходящие структуры данных, делать лишние системные вызовы и писать собственные циклы вместо готовых функций. Поменяйте список на set, соберите строки в список, откройте файл один раз, а остальное будет работать быстрее без лишних усилий.
