6 .5 . Генераторы — это
упрощенные итераторы 229
'Всем привет'
>>> next(iterator)
'Всем привет'
>>> next(iterator)
StopIteration
>>> next(iterator)
StopIteration
Этот итератор вел себя именно так, как мы и ожидали. Как только мы до-
стигаем конца функции-генератора, он начинает вызывать
StopIteration
,
сигнализируя о том, что у него больше нет значений, которые он мог бы
предоставить.
Давайте вернемся к еще одному примеру из раздела об итераторах. Класс
BoundedIterator
реализовал итератор, который будет повторять значение,
заданное определенное количество раз:
class BoundedRepeater:
def __init__(self, value, max_repeats):
self.value = value
self.max_repeats = max_repeats
self.count = 0
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_repeats:
raise
StopIteration
self.count += 1
return self.value
Почему бы не попробовать реализовать класс
BoundedRepeater
заново как
функцию-генератор? Сделаю первую попытку:
def bounded_repeater(value, max_repeats):
count = 0
while True:
if count >= max_repeats:
return
count += 1
yield value
230 Глава 6 •
Циклы и итерации
Я преднамеренно сделал цикл
while
в этой функции несколько громозд-
ким. Я хотел продемонстрировать, как вызов
инструкции
return
из ге-
нератора приводит к остановке итераций с исключением
StopIteration
.
Мы вскоре подчистим и еще немного упростим эту функцию-генератор,
но сначала давайте испытаем то, что у нас есть сейчас:
>>> for x in bounded_repeater('Привет', 4):
... print(x)
'Привет'
'Привет'
'Привет'
'Привет'
Великолепно! Теперь у нас есть генератор, который прекращает по-
рождать значения после настраиваемого количества повторений. Он
использует инструкцию
yield
, чтобы передавать значения назад до тех
пор, пока он наконец не натолкнется на инструкцию
return
и итерации
не прекратятся.
Как я вам обещал, мы можем упростить этот генератор еще больше. Мы
воспользуемся тем, что в конец каждой функции Python добавляет не-
явную инструкцию
return
None
. И вот как будет выглядеть наша оконча-
тельная реализация:
def bounded_repeater(value, max_repeats):
for i in range(max_repeats):
yield value
Не
стесняйтесь подтвердить, что этот
упрощенный генератор по-
прежнему работает таким же образом. Учитывая все обстоятельства, мы
прошли путь от 12-строчной реализации в классе
BoundedRepeater
до
трехстрочной реализации на основе генератора, обеспечив ту же самую
функциональность. А это 75 %-ное сокращение количества строк кода —
нехило!
Как вы только что убедились, генераторы помогают «абстрагироваться
от» большей части шаблонного кода, который в других обстоятельствах
был бы необходим во время написания итераторов на основе класса. Они
способны очень облегчить вашу программистскую жизнь и позволяют
6 .6 . Выражения-генераторы
231
писать более чистые, короткие и удобные в сопровождении итераторы.
Функции-генераторы представляют собой отличное функциональное
средство языка Python, и вам следует решительно и смело использовать
их в своих собственных программах.
Ключевые
выводы
Функции-генераторы являются синтаксическим
сахаром для написа-
ния объектов, которые поддерживают протокол итератора. Генераторы
абстрагируются от большей части шаблонного кода, необходимого во
время написания итераторов на основе класса.
Инструкция
yield
позволяет временно приостанавливать исполнение
функции-генератора и передавать из него значения назад.
Генераторы начинают вызывать исключения
StopIteration
после того,
как поток управления покидает функцию-генератор каким-либо иным
способом, кроме инструкции
yield
.
6 .6 . Выражения-генераторы
По мере того как я все больше узнавал о протоколе итератора Python
и различных способах его реализации в собственном коде, я стал пони-
мать, что синтаксический сахар является повторяющейся темой.
Дело в том, что итераторы на основе класса и функции-генераторы вы-
ражают один и тот же лежащий в основе шаблон проектирования.
Функции-генераторы предоставляют краткую форму для поддержки
протокола итератора в своем собственном коде и по большей части
избегают многословности итераторов на основе класса. Благодаря не-
значительному объему специализированного синтаксиса или «горсти»
Do'stlaringiz bilan baham: