Короткий ответ
List comprehension сразу создаёт список со всеми элементами в памяти, а генераторное выражение создаёт ленивый объект, который вычисляет элементы по одному при обходе, не храня всю последовательность целиком.
Как это работает подробнее
Синтаксически они почти идентичны — [x for x in it] против (x for x in it) — но квадратные скобки материализуют весь результат немедленно, а круглые создают генератор, вычисляющий следующее значение только при вызове next().
Пример кода
example-244.py
import sys
lst = [x * x for x in range(1_000_000)]
gen = (x * x for x in range(1_000_000))
print(sys.getsizeof(lst)) # существенно больше (десятки МБ)
print(sys.getsizeof(gen)) # константно малый размер независимо от диапазонаЧто использовать на практике
- Практика. Для одноразовой агрегации (
sum,any,join) выгоднее генераторное выражение — не нужно хранить промежуточный список. Если данные нужны несколько раз, требуетсяlen()или индексация — используется list comprehension или явныйlist(...).
Подводные камни
- Риски. Оборачивают генераторное выражение в
list()сразу после создания без необходимости, теряя выигрыш в памяти; передают генераторное выражение туда, где ожидается повторный проход, получая пустой результат во второй раз.