Итераторы и генераторы
Выдавайте значения по одному, по требованию, не строя весь список целиком
return на yield, и Python отдаёт вам ленивый, экономный по памяти поток значений.
- объяснить протокол итератора (
__iter__,__next__,StopIteration), - отличить итерируемый объект от итератора,
- написать функцию-генератор с
yieldи проследить, как она приостанавливается и возобновляется, - рассуждать о ленивых вычислениях и о том, почему они экономят память,
- использовать компактные генераторные выражения и знать, когда они лучше списочного включения.
1. Что на самом деле происходит в цикле for
Вы писали for x in items: сотни раз. На деле Python выполняет танец из двух шагов. Сначала он вызывает iter(items), чтобы получить итератор. Затем на каждом проходе он вызывает у этого итератора next(), чтобы вытянуть одно значение. Когда у итератора ничего не остаётся, он возбуждает специальное исключение StopIteration, и цикл тихо завершается.
Этот танец можно выполнить вручную:
nums = [10, 20, 30]
it = iter(nums) # получить итератор из списка
print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
print(next(it)) # возбуждает StopIteration
Цикл for — это ровно тот же приём, только StopIteration перехватывается за вас. Понимание этого объясняет почти всё остальное в этой теме.