Генераторы, iter() и идентичность
@python_quizРазберем этот квиз
Коротко: в коде используется генераторное выражение, и ключевой момент — генератор уже является итератором, поэтому вызов iter() возвращает тот же объект. Также генератор одноразовый: если в нём нет элементов, попытка получить из него что-либо сразу приведёт к StopIteration и список окажется пустым.
Исходный код

Разберём по шагам, что тут происходит.
Пошаговый разбор
- g = (x for x in []) — создаётся генератор (generator object). Генераторное выражение всегда возвращает объект-генератор.
- Генератор реализует протокол итератора: у него есть метод
__iter__(), который возвращает сам объект. Поэтому iter(g) возвращает g. it1 = iter(g);it2 = iter(it1) — оба присваивания дают один и тот же объект. Проверка it1 is it2 вернёт True.- Генератор перебирает элементы по требованию. В данном случае он создан из пустого итерируемого, поэтому при первой попытке получить элемент он сразу бросит StopIteration.
- list(it2) пытается пройти по оставшимся элементам генератора и собрать их в список. Поскольку элементов нет, результат — пустой список [].
Итого: выражение напечатает True и пустой список.
Демонстрация в коде
Оригинальный пример:

Пример с ненулевым генератором:

Контраст: список (итерируемый, но не итератор)

Контраст: пользовательский итерируемый класс, возвращающий новый итератор:

Выводы и полезные наблюдения
- Генератор (generator object) — это итератор: вызов iter(generator) возвращает сам генератор.
- Для генераторов it1 is iter(it1) будет True.
- Генераторы одноразовые: после полного обхода дальнейшие попытки чтения вернут StopIteration, и list(…) вернёт пустой список.
- Для обычных итерируемых объектов (список, строка, пользовательские контейнеры) вызов iter() обычно создаёт новый независимый итератор — поэтому identity будет False.
Такое поведение важно помнить при проектировании кода: если нужен многократный проход по данным — используйте коллекции или создавайте новый итератор заново; если нужен ленивый одноразовый поток — подойдёт генератор.