Вопрос проверяет понимание различий между последовательностями и генераторами.
Генератор не хранит все элементы в памяти, он вычисляет их по одному. У него нет структуры данных, где можно взять элемент по позиции. Поэтому операции вроде gen[3] не поддерживаются. Чтобы получить элементы, нужно последовательно итерироваться.
Генераторы устроены иначе, чем списки или кортежи.
Определение: Генератор — это итератор, который вычисляет значения “на лету” (lazy evaluation), а не хранит их заранее.
Нет хранения элементов
Список:
lst = [x * 2 for x in range(10)]
print(lst[3]) # элемент уже в памяти
Генератор:
gen = (x * 2 for x in range(10))
# gen[3] -> TypeError
Значения могут ещё не существовать
Генератор может вычислять значения только при запросе, и заранее их просто нет.
Генератор одноразовый
Если пройти несколько элементов, они “потреблены” и назад вернуться нельзя без создания нового генератора.
Если нужно взять элемент по позиции, обычно делают:
from itertools import islice
gen = (x * 2 for x in range(100))
value = next(islice(gen, 5, None)) # шестой элемент
большие последовательности;
потоковые данные;
ситуации, где важна экономия памяти.
К генератору нельзя обращаться по индексу, потому что он не хранит элементы и не поддерживает произвольный доступ — только последовательную итерацию.