Генераторы и итераторы в Python
В этом уроке вы узнаете о генераторах Python и о том, как использовать генераторы для создания итераторов.
- Что такое генераторы в Python?
- Простой пример генератора Python
- Использование генераторов 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)