В этом уроке вы узнаете о генераторах Python и о том, как использовать генераторы для создания итераторов.
Что такое генераторы в Python?
Обычно Python выполняет обычную функцию сверху вниз на основе модели выполнения до завершения.
Это означает, что Python не может приостановить выполнение обычной функции на полпути, а затем возобновить ее выполнение после этого. Например:
def greeting():
print('Hi!')
print('How are you?')
print('Are you there?')
Когда Python выполняет функцию Greeting(), он выполняет код построчно сверху вниз.
Кроме того, Python не может приостановить выполнение функции на следующей строке:
print('How are you?')
… и переходит к другому коду и возобновляет выполнение с этой строки.
Чтобы приостановить выполнение функции на полпути и возобновить ее выполнение с того места, где она была приостановлена, вы используете оператор yield.
Когда функция содержит хотя бы один оператор yield, это функция-генератор. По определению, генератор — это функция, содержащая хотя бы один оператор yield.
Когда вы вызываете функцию-генератор, она возвращает новый объект-генератор. Однако функция не запускается. Объекты-генераторы (или генераторы) реализуют протокол итератора. По сути, генераторы — это ленивые итераторы. Поэтому, чтобы выполнить функцию-генератор, вы вызываете для нее встроенную функцию next().
Простой пример генератора Python
См. следующий пример:
def greeting():
print('Hi!')
yield 1
print('How are you?')
yield 2
print('Are you there?')
yield 3
Поскольку функция Greeting() содержит оператор yield, это функция-генератор. Оператор yield похож на оператор return в функции. Однако есть большая разница.
Когда Python встречает оператор yield, он возвращает значение, указанное в выводе. Кроме того, он приостанавливает выполнение функции. Если вы «вызовете» ту же функцию еще раз, Python возобновит работу с того места, где был обнаружен предыдущий оператор.
Когда вы вызываете функцию-генератор, она возвращает объект-генератор. Например:
messenger = greeting()
messenger — это объект-генератор, который также является итератором.
Чтобы фактически выполнить тело функции Greeting(), вам необходимо использовать встроенную функцию next():
result = next(messenger) print(result)
Когда вы выполняете функцию Greeting(), она отображает первое сообщение и возвращает 1:
Hi! 1
Кроме того, она приостанавливается прямо в первом операторе yield. Если вы «вызовете» функцию Greeting() еще раз, она возобновит выполнение с последнего оператора yield:
result = next(messenger) print(result)
Выход:
How are you? 2
И вы можете вызвать его снова:
result = next(messenger) print(result)
Выход:
Are you there? 3
Однако если вы запустите генератор еще раз, он вызовет исключение StopIteration, поскольку это итератор:
next(messenger)
Ошибка:
StopIteration
Использование генераторов Python для создания итераторов
В следующем примере определяется итератор, который возвращает квадратное число целого числа.
class Squares:
def __init__(self, length):
self.length = length
self.current = 0
def __iter__(self):
return self
def __next__(self):
result = self.current ** 2
self.current += 1
if self.current > self.length:
raise StopIteration
return result
И вы можете использовать итератор Squares для генерации квадратных чисел первых 5 целых чисел от 0 до 5:
length = 5
square = Squares(length)
for s in square:
print(s)
Этот код работает так, как мы ожидали. Но есть одна проблема: здесь много шаблонов. И, как вы могли догадаться, для создания этого итератора мы используем функцию-генератор.
Следующий код переписывает итератор Squares как функцию-генератор:
def squares(length):
for n in range(length):
yield n ** 2
Как видите, код гораздо короче и выразительнее. Использование функции генератора квадратов аналогично итератору выше:
length = 5
square = squares(length)
for s in square:
print(s) 