Генератор — одноразовый итератор: почему второй list() пуст
@python_quizРазберем этот квиз
Код, о котором идёт речь:

Кратко — первый вызов list(c) напечатает [0, 1, 2], второй — пустой список. Ниже объясню подробно почему и разберу варианты ответов.
Что делает этот код — пошагово
c = (i for i in range(3))— создаётся генератор (generator expression). Генератор реализует протокол итератора: у него есть состояние и он выдаёт элементы по запросу.list(c)— начинает итерировать генераторc, получает элементы 0, 1, 2 и собирает их в список. Генератор при этом полностью исчерпывается.- Второй
list(c)— пытается итерировать тот же самый объект-генератор, но он уже в состоянии «исчерпан» (больше элементов нет), поэтому возвращается пустой список.
Почему генератор одноразовый
- Генератор — это итератор. Итераторы сохраняют текущее состояние обхода.
- После прохода по всем элементам итератор считается завершённым; последующие попытки получить элементы возвращают StopIteration, поэтому коллекции, построенные из такого итератора, пусты.
Анализ вариантов ответов (логика)
- Вариант, где оба вызова дают полный список, предполагает, что
c— не итератор, а «многократный» iterable (например, список). Это неверно для генератора. - Вариант, где первый пуст, а второй — полный, противоречит порядку выполнения: первый вызов как раз потребляет элементы.
- Вариант, где оба пусты, означал бы, что генератор был пуст изначально — но он генерирует 3 значения.
- Правильный вывод программы: сначала список из трёх элементов, затем пустой список.
Итого: вывод будет

Как получить повторяемый результат — варианты решения
Если требуется несколько раз получить одинаковые списки, можно:
- Сохранить значения в список один раз:

- Использовать list comprehension сразу (это создаёт список, а не генератор):

- Создавать новый генератор при каждом обходе:

- Если нужно «разветвить» итератор на несколько независимых итераторов — использовать itertools.tee (учтите предполагаемую память для буферизации):

Дополнительные замечания
- range(3) сам по себе — итерируемый объект, но не итератор (вызов iter(range(3)) вернёт новый итератор). Генератор же сразу возвращает итератор.
- Если генератор большой или вычисление значений дорогое, материализация в список может быть нежелательной с точки зрения памяти — в таких случаях планируйте, как и где вы будете потреблять элементы.
Вывод
Генераторы — удобный инструмент для ленивой генерации значений, но важно помнить: они одноразовы. Если нужно повторно пройти по результатам — материализуйте их в контейнер или создавайте новый генератор для каждого обхода.