На первый взгляд кажется, что оба варианта должны работать с одинаковой скоростью, так как они выполняют по сути одно и то же. Однако в действительности разница во времени выполнения может быть связана с деталями работы Python. Давайте разберем это по шагам:
### В чем различие между функциями?
1. **`test1`**:
res = [i for i in list(range(10000)) if i % 2 == 0]
Здесь `range(10000)` сначала преобразуется в список с помощью функции `list()`, а затем этот список используется в списочном выражении (`list comprehension`).
2. **`test2`**:
l = list(range(10000))
res = [i for i in l if i % 2 == 0]
Здесь `list(range(10000))` создается заранее и сохраняется в переменную `l`, а затем используется в списочном выражении.
### Почему `test2` быстрее?
1. **Обращение к встроенным функциям:**
В первом случае (`test1`) вызов `list(range(10000))` происходит **внутри списочного выражения**, что добавляет некоторую дополнительную нагрузку. Python вынужден каждый раз вызывать встроенную функцию `list()` и одновременно работать с результатом.
2. **Оптимизация промежуточных объектов:**
Во втором случае (`test2`) переменная `l` хранит уже готовый список. Python обращается к уже созданному объекту, что уменьшает накладные расходы на повторное выполнение `list(range(10000))`.
3. **Повторная работа с объектом:**
В первом случае Python обрабатывает генерацию списка и фильтрацию "на лету". Это может быть менее эффективно, поскольку требует больше переключений контекста.
4. **Кэширование объектов:**
В `test2` список `l` уже находится в оперативной памяти и готов к использованию. В `test1` каждый раз создается временный объект, что может быть менее оптимально с точки зрения времени выполнения.
### Как проверить разницу объективно?
Если вы хотите измерить разницу времени выполнения более точно, используйте модуль `timeit`, который специально предназначен для измерения производительности:
import timeit
setup_code = "l = list(range(10000))"
test1_code = "[i for i in list(range(10000)) if i % 2 == 0]"
test2_code = "[i for i in l if i % 2 == 0]"
print("test1:", timeit.timeit(test1_code, number=1000, globals=globals()))
print("test2:", timeit.timeit(test2_code, setup=setup_code, number=1000, globals=globals()))
### Итог
Разница во времени выполнения связана с тем, что `test1` каждый раз создает новый список с помощью `list(range(10000))`, а в `test2` список создается один раз и используется повторно. Это объясняет, почему `test2` работает быстрее.
Если вам важно максимизировать производительность, старайтесь избегать излишнего создания временных объектов внутри списочных выражений или циклов.