Генераторы в 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
  • Функция-генератор возвращает объект-генератор.
  • Объект генератор — это итератор. Поэтому он становится «исчерпывается», когда не остается ни одного элемента, который можно вернуть.
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©