Генераторы в Python
В этой статье вы узнаете, что такое генераторы и как их использовать для создания итераторов в Python.
Как правило, Python выполняет обычную функцию сверху вниз по модели run-to-completion.
Это означает, что Python не может приостановить выполнение обычной функции на середине пути, а затем возобновить ее выполнение.
Рассмотрим на примере функции greeting()
.
def greeting():
print('Привет!')
print('Как дела?')
print('Ты тут?')
Когда Python выполняет функцию greeting()
, он выполняет код построчно сверху вниз.
То есть приостановить выполнение функции, например, на строке print('Как дела?')
Python не может.
Чтобы приостановить функцию на середине пути и возобновить ее с того места, где она была приостановлена, используется оператор yield
.
Генераторная функция — любая функция в Python, в теле которой встречается ключевое слово
yield
.
Когда вы вызываете функцию-генератор, она возвращает новый объект-генератор. Но этот объект не запускает функцию.
Объекты-генераторы (или генераторы) реализуют протокол итератора. Генераторы — это такие ленивые итераторы. Так что чтобы выполнить функцию-генератор, нужно вызвать встроенную функцию next()
.
Пример генератора
def greeting():
print('Привет!!')
yield 1
print('Как дела?')
yield 2
print('Ты тут?')
yield 3
Поскольку в функции greeting()
содержится оператор yield
, она является генераторной функцией.
Оператор yield
похож на оператор return
в функция, но со следующим отличием.
Когда Python встречает оператор yield
, он возвращает значение, указанное в операторе yield
. Затем он приостанавливает выполнение функции.
Если вы снова «вызовете» ту же функцию, Python продолжит выполнение с того места, где он встретил предыдущий оператор yield
.
Когда вы вызываете функцию генератора, она возвращает объект генератора. Например:
messenger = greeting()
Здесь messenger
— это объект-генератор, который также является итератором.
Чтобы фактически выполнить тело функции greeting()
, необходимо использовать встроенную функцию next()
:
result = next(messenger)
print(result)
Когда функция greeting()
начнет выполняться, она сначала покажет первое сообщение и вернет 1.
Вывод
Привет!
1
При этом функция приостанавится прямо на первом операторе yield
. Если вы снова «вызовете» функцию greeting()
, она возобновит выполнение с последнего оператора yield
:
result = next(messenger)
print(result)
Вывод
Как дела?
2
Если «вызвать» функцию в третий раз, она возобновит работу со второго yield
: выведет соответсвующее сообщение и вернет 3:
result = next(messenger)
print(result)
Вывод
Ты тут?
3
Если вы попробуете выполнить генератор еще раз, он вызовет исключение StopIteration, поскольку является итератором:
next(messenger) # ошибка StopIteration
Создание итераторов с помощью генераторов
Давайте создадим итератор Squares
, который будет возвращать квадрат целого числа.
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
Как видите, она намного короче и выразительнее. Использовать функцию-генератор squares()
можно так же, как мы использовали итератор выше.
length = 5
square = squares(length)
for s in square:
print(s)
Что нужно запомнить
- Генератор — это функция, в которой содержится хотя бы один оператор
yield
. - Функция-генератор возвращает объект-генератор.
- Объект генератор — это итератор. Поэтому он становится «исчерпывается», когда не остается ни одного элемента, который можно вернуть.