Кратко
Секция статьи "Кратко"Генератор — это функция, которая умеет возвращать несколько значений по очереди, не храня в памяти весь набор значений.
Пример
Секция статьи "Пример"Вернёмся к вашей реализации Range
: у вас есть класс, который умеет возвращать следующее число в заданном диапазоне. В методе
вы каждый раз высчитываете актуальное значение и возвращаете его пользователю. Формально это можно назвать генератором. Но в Python для создания генераторов используется особый, упрощенный синтаксис.
Давайте посмотрим, как тот же самый код будет выглядеть, если написать его с использованием генератора.
def gen_range(stop_value): stop_value = stop_value - 1 current = -1 while current < stop_value: current += 1 yield current
def gen_range(stop_value): stop_value = stop_value - 1 current = -1 while current < stop_value: current += 1 yield current
Запустим код и убедимся в этом.
for x in gen_range(3): print(x)# 0# 1# 2
for x in gen_range(3): print(x) # 0 # 1 # 2
Как это работает
Секция статьи "Как это работает"Работа генераторов построена на принципе запоминания контекста выполнения функции. Если не вдаваться в подробности работы Python-интерпретатора, то функция-генератор «запоминает», на каком месте она остановилась, и может продолжить своё выполнение после ключевого слова yield
.
def simple_generator(): yield 1 yield 2 return 3
def simple_generator(): yield 1 yield 2 return 3
Рассмотрим, как поведёт себя генератор с несколькими yield:
Изучая итераторы вы узнали, что доставать из него значения можно вручную с помощью метода next или циклом, а ещё неявно — в цикле. Генератор предоставляет те же возможности: любой генератор является итератором. В предыдущем примере вы воспользовались циклом, а сейчас, для разнообразия, достанем элементы вручную.
gen = simple_generator()print(next(gen))print(next(gen))print(next(gen))
gen = simple_generator() print(next(gen)) print(next(gen)) print(next(gen))
В результате вы увидите следующее:
12Traceback (most recent call last):...StopIteration: 3
1 2 Traceback (most recent call last): ... StopIteration: 3
То есть функция действительно запоминает, где она остановилась после каждого вызова функции next.
Генераторы удобны и для создания генераторных выражений — generator expressions
.
Особенно это полезно, если нужно сгенерировать много объектов, а память расходовать жалко. Код выглядит так:
gen_exp = (x for x in range(100000))print(gen_exp)<generator object <genexpr> at 0x10c4496d0>
gen_exp = (x for x in range(100000)) print(gen_exp) <generator object <genexpr> at 0x10c4496d0>