Генераторы и итераторы в 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)
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *