Короткий ответ
List comprehension [x for x in it] сразу вычисляет и хранит в памяти весь список, а generator expression (x for x in it) создаёт ленивый итератор, который вычисляет элементы по одному при обращении — выбор зависит от того, нужен ли полный список сразу или достаточно пройти по данным один раз.
Как это работает подробнее
List comprehension выполняет цикл немедленно и собирает результаты в новый объект list в памяти. Generator expression возвращает объект-генератор без вычислений "внутри"; каждое значение вычисляется лениво при вызове next() (явном или через for/sum/list и т.п.), и после одного полного прохода генератор исчерпывается.
Пример кода
example.py
numbers = range(1_000_000)
squares_list = [n * n for n in numbers] # весь список сразу в памяти
squares_gen = (n * n for n in numbers) # ленивый генератор, почти без памяти
total = sum(n * n for n in numbers) # эффективно: без промежуточного списка
gen = (x for x in [1, 2, 3])
print(list(gen)) # [1, 2, 3]
print(list(gen)) # [] — генератор уже исчерпан, повторный проход невозможенЧто использовать на практике
- Практика. Для больших или потенциально бесконечных источников данных (построчное чтение файла, поток записей из БД) генератор экономит память, так как не держит все элементы одновременно. Если результат нужно индексировать, проходить несколько раз, получить len() или передать как JSON — нужен конкретный список, то есть list comprehension или
list(generator).
Подводные камни
- Риски. Пытаются повторно проитерироваться по уже исчерпанному генератору и получают пустой результат без ошибки. Также создают list comprehension там, где нужен только один проход по большим данным (например, суммирование), и получают лишний расход памяти на промежуточный список.