← Назад к вопросам
Как оптимизировать перебор списка?
2.3 Middle🔥 161 комментариев
#Python Core#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как оптимизировать перебор списка
Перебор (iteration) — одна из самых частых операций в коде. Неправильно написанный перебор может сильно замедлить приложение. Покажу реальные техники оптимизации.
1. Основные проблемы неоптимизированного кода
# ❌ Плохо: множественные обращения к атрибутам
result = []
for i in range(len(my_list)):
result.append(my_list[i] * 2) # Доступ к атрибуту my_list
# ❌ Плохо: функции внутри цикла
for item in items:
print(len(item)) # len() вызывается каждый раз!
# ❌ Плохо: конкатенация строк
result = ""
for word in words:
result = result + word # Создаёт новую строку каждый раз!
2. List comprehensions vs циклы
List comprehension быстрее обычного цикла:
import timeit
# Способ 1: обычный цикл
def method1():
result = []
for i in range(1000):
result.append(i * 2)
return result
# Способ 2: list comprehension
def method2():
return [i * 2 for i in range(1000)]
print(timeit.timeit(method1, number=10000)) # 0.45 сек
print(timeit.timeit(method2, number=10000)) # 0.28 сек
# List comprehension на 60% быстрее!
Правило: используй list comprehension где возможно:
# ✅ Быстро
squares = [x ** 2 for x in range(1000)]
# ✅ С условием
evens = [x for x in range(1000) if x % 2 == 0]
# ✅ С преобразованием
names_upper = [name.upper() for name in names]
# ✅ Генератор для больших данных
squares_gen = (x ** 2 for x in range(1000000)) # Не хранит всё в памяти
3. map() и filter() для функциональной обработки
# ❌ Медленнее
result = []
for item in items:
if is_valid(item):
result.append(transform(item))
# ✅ Быстрее
result = list(map(transform, filter(is_valid, items)))
# Ещё быстрее (ленивое вычисление)
result_lazy = map(transform, filter(is_valid, items))
for item in result_lazy:
print(item) # Вычисляется по мере необходимости
4. Локальные переменные работают быстрее
# ❌ Медленно: каждый раз ищет my_list.append в глобальной области
my_list = []
for i in range(1000000):
my_list.append(i)
# ✅ Быстро: append сохранён в локальной переменной
my_list = []
append_method = my_list.append # Локальная переменная
for i in range(1000000):
append_method(i)
# На 10-15% быстрее!
5. Избегай множественных обращений к атрибутам
# ❌ Медленно
def process_users(users):
result = []
for user in users:
if user.active and user.verified and user.admin:
result.append(user.name.upper() + user.email.lower())
return result
# ✅ Быстро: кэшируем атрибуты
def process_users(users):
result = []
for user in users:
# Вытаскиваем атрибуты в локальные переменные
if user.active and user.verified and user.admin:
name = user.name
email = user.email
result.append(name.upper() + email.lower())
return result
# Или ещё лучше
def process_users(users):
return [
f"{user.name.upper()}{user.email.lower()}"
for user in users
if user.active and user.verified and user.admin
]
6. NumPy для работы с большими наборами данных
import numpy as np
import timeit
# Способ 1: обычный Python список
def python_method():
arr = list(range(1_000_000))
return [x * 2 for x in arr]
# Способ 2: NumPy массив
def numpy_method():
arr = np.arange(1_000_000)
return arr * 2
print(timeit.timeit(python_method, number=10)) # 0.85 сек
print(timeit.timeit(numpy_method, number=10)) # 0.015 сек
# NumPy в 50 раз быстрее!
7. Итерирование с индексом оптимально
# ❌ Медленно: len() и индексирование
for i in range(len(items)):
print(items[i])
# ✅ Быстро: прямая итерация
for item in items:
print(item)
# ✅ Если нужны индексы: enumerate()
for i, item in enumerate(items):
print(f"{i}: {item}")
8. zip() для одновременной итерации
# ❌ Медленно
for i in range(len(names)):
print(names[i], ages[i])
# ✅ Быстро и понятнее
for name, age in zip(names, ages):
print(name, age)
9. Конкатенация строк
import timeit
# ❌ Очень медленно: O(n²)
def slow_concat():
result = ""
for word in ["word"] * 1000:
result = result + word
return result
# ✅ Быстро: O(n)
def fast_concat():
return "".join(["word"] * 1000)
# ✅ С преобразованием: для больших данных
def fast_concat_gen():
return "".join(str(x) for x in range(1000))
print(timeit.timeit(slow_concat, number=100)) # 0.5 сек
print(timeit.timeit(fast_concat, number=100)) # 0.0001 сек
# join() в 5000 раз быстрее!
10. Выбор правильной структуры данных
# ❌ Медленно: O(n) для поиска в списке
if item in my_list: # Проверяет каждый элемент
pass
# ✅ Быстро: O(1) для поиска в set
if item in my_set: # Хэширование
pass
# Сравнение
import timeit
my_list = list(range(10000))
my_set = set(range(10000))
print(timeit.timeit(
lambda: 9999 in my_list,
number=100000
)) # 0.3 сек
print(timeit.timeit(
lambda: 9999 in my_set,
number=100000
)) # 0.001 сек
# Set в 300 раз быстрее!
11. Полная оптимизация: пример
# Задача: найти все уникальные пары пользователей, у которых одинаковый статус
# ❌ Очень медленно: O(n³)
def slow_approach(users):
result = []
for i in range(len(users)):
for j in range(len(users)):
if users[i].status == users[j].status and i != j:
pair = (users[i].id, users[j].id)
if pair not in result:
result.append(pair)
return result
# ✅ Быстро: O(n)
def fast_approach(users):
from collections import defaultdict
# Группируем по статусу
by_status = defaultdict(list)
for user in users:
by_status[user.status].append(user.id)
# Находим пары
result = set()
for status, user_ids in by_status.items():
for i, uid1 in enumerate(user_ids):
for uid2 in user_ids[i+1:]:
result.add((min(uid1, uid2), max(uid1, uid2)))
return list(result)
Чеклист оптимизации
- Профилируй — py-spy, cProfile
- Используй list comprehension вместо циклов
- Кэшируй атрибуты в локальных переменных
- Используй map/filter для функциональной обработки
- Выбирай правильную структуру (set vs list)
- NumPy для больших данных
- join() для строк, не конкатенацию
- Генераторы для больших последовательностей
- Избегай множественных вызовов функций в цикле
- Тестируй с реальными данными
Вывод
Можно улучшить перебор на 10-100x не меняя алгоритм. Начни с профилирования, потом применяй техники выше. Помни: микро-оптимизации важны только если они показывают результаты на профайлере.