6 .5 . Генераторы — это
упрощенные итераторы 227
def repeater(value):
while True:
yield value
И это вполне подходящая ментальная модель того, что здесь происходит.
Дело в том, что, когда инструкция
return
вызывается внутри функции,
она безвозвратно передает управление назад источнику вызова функции.
Когда же вызывается инструкция
yield
, она тоже передает управление
назад источнику вызова функции — но
она это делает лишь временно.
В отличие от инструкции
return
, которая избавляется от локального
состояния функции, инструкция
yield
приостанавливает функцию и со-
храняет ее локальное состояние. На практике это означает, что локальные
переменные и состояние исполнения функции-генератора лишь откла-
дываются в сторону и не выбрасываются полностью. Исполнение может
быть возобновлено в любое время вызовом функции
next()
с генератором
в качестве аргумента:
>>> iterator = repeater('Привет')
>>> next(iterator)
'Привет'
>>> next(iterator)
'Привет'
>>> next(iterator)
'Привет'
Это делает генераторы полностью совместимыми с протоколом итератора.
По этой причине мне нравится представлять их прежде всего как синтак-
сический сахар для реализации итераторов.
Вы убедитесь, что в отношении большинства типов итераторов написание
функции-генератора будет проще, а восприятие легче, чем определение
многословного итератора на основе класса.
Генераторы,
которые прекращают генерацию
Этот раздел мы начали с того, что еще раз написали
бесконечный генера-
тор. Сейчас вы, вероятно, задаетесь вопросом, как написать генератор,
который через некоторое время прекращает порождать значения вместо
того, чтобы без конца продолжать это делать.
228 Глава 6 •
Циклы и итерации
Напомним, что в нашем итераторе на основе класса мы смогли подать
сигнал об окончании итераций путем вызова исключения
StopIteration
вручную. Поскольку генераторы полностью совместимы с итераторами
на основе класса, за сценой будет по-прежнему происходить то же самое.
К счастью, на этот раз мы будем работать с более приятным интерфейсом.
Генераторы прекращают порождать значения, как только поток управле-
ния возвращается из функции-генератора каким-либо иным способом,
кроме инструкции
yield
. Это означает, что вам больше вообще не нужно
заботиться
о вызове исключения
StopIteration
!
Приведу пример:
def repeat_three_times(value):
yield value
yield value
yield value
Обратите внимание: эта функция-генератор не содержит никакого цикла.
В действительности она проста как божий день и состоит всего из трех
инструкций
yield
. Если
yield
временно приостанавливает выполнение
функции и передает значение назад источнику вызова, то что произойдет,
когда мы достигнем конца этого генератора? Давайте узнаем:
>>> for x in repeat_three_times('Всем привет'):
... print(x)
Do'stlaringiz bilan baham: