← Все вопросы

Чем генераторное выражение (x for x in ...) отличается от list comprehension?

Задан 30 месяцев назад428 просмотров2 ответа
5

Вижу в коде два почти одинаковых варианта и не пойму, в чём разница, кроме скобок:

a = [x * x for x in range(1_000_000)]   # квадратные скобки
b = (x * x for x in range(1_000_000))   # круглые скобки

Печать b выдаёт что-то типа <generator object ...>, а не список. Когда какой брать? Это про память?

2 ответа

10
✓ Принятый ответ — помог автору

Да, разница по сути одна, но важная: ленивость и память.

  • [...] (list comprehension) сразу строит весь список в памяти — миллион элементов лежат целиком.
  • (...) (генераторное выражение) ничего не вычисляет заранее. Это итератор, который выдаёт элементы по одному, по запросу. В памяти он почти ничего не занимает, пока ты его не крутишь.
import sys

a = [x * x for x in range(1_000_000)]
b = (x * x for x in range(1_000_000))

print(sys.getsizeof(a))  # несколько мегабайт
print(sys.getsizeof(b))  # ~200 байт — сам объект-генератор, элементов ещё нет

Когда что брать:

  • Нужен список целиком (индексация a[5], повторный проход, len()) → list comprehension.
  • Надо просто пройтись один раз, особенно по большому/бесконечному источнику, или сразу свернуть в sum/max/any → генераторное выражение, сэкономишь память.

Отдельный приятный момент: если генераторное выражение — единственный аргумент функции, скобки можно не дублировать:

total = sum(x * x for x in range(1_000_000))   # без лишних скобок, лениво

Главная ловушка генератора: он одноразовый. Прошёлся по нему один раз — он опустел, второй раз for по нему ничего не даст. Список так можно гонять сколько угодно.

4

Добавлю быстрый ориентир «когда генератор не нужен»: на маленьких коллекциях list comprehension часто даже чуть быстрее и удобнее (можно индексировать, отлаживать). Ленивость окупается на больших данных или когда ты прерываешь обход раньше конца (next(), break, any(...)) — тогда генератор не считает лишнего.

И помни про одноразовость: если случайно сохранил генератор в переменную и прошёлся по нему дважды — на второй раз получишь пусто, без ошибки. Очень частый баг.

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект