Как использовать генераторы в языке Python

Содержание:

Генераторы Python — класс функций, которые позволяют создавать и хранить в памяти собственные итераторы, возвращающие значение функций по запросу. Генераторы обрабатывают данные порционно, шаг за шагом, и применяются при работе с большими объемами информации, когда возникает риск переполнения памяти. Необходимость использования генераторов может возникнуть в следующих случаях:

  • работа с большими файлами (десятки гигабайт): сравнение строк нескольких таких файлов, обработка, поиск по условию и т. д.;
  • веб-скрейпинг;
  • анализ бесконечного потока данных (сетевой трафик, биржевые котировки, показания приборов, датчиков);
  • математические вычисления;
  • рефакторинг (переработка) существующего кода.

Не стоит путать генераторы как объекты (функции) со следующими конструкциями:

  • генераторы списков;
  • генераторы множеств.

Списки и множества не имеют отношения к генераторам-объектам. В данном случае слово «генератор» выступает лишь синтаксической конструкцией, возникающей при переводе с английского «list comprehension».

Рассмотрим сначала генераторы списков и множеств, чтобы в дальнейшем не возникало путаницы при использовании непосредственно самих генераторов.

Генераторы списков

Для того чтобы создать длинный список программным методом, используется цикл с дополнительным условием. Выражение заключается в квадратные скобки и в общем виде будет выглядеть как:

[выражение for item in итерируемое множество if условие].

Создадим сначала список чисел от 1 до 30, а затем из этого диапазона выберем только четные числа:

nums = [i for i in range(1, 10)]

even_nums = [i for i in range(1, 10) if n % 2 == 0]

print(nums)

print(even_nums)

>

[1, 2, 3, 4, 5, 6, 7, 8, 9]

[2, 4, 6, 8]

В примере выражение является самим элементом item, числа от 1 до 30 — итерируемое множество, делимость нацело без остатка — условие.

Генераторы списков Python относят к «синтаксическому сахару» и используют для сокращения количества кода.

Генераторы множеств

Представляют собой выражение вида:

{выражение for item in итерируемое множество if условие}.

Как видим, от списков отличается только наличием фигурных скобок. Генераторы множеств можно использовать для того, чтобы отфильтровать ненужные элементы в каком-то множестве. Например, нам нужно отобрать файлы с расширением txt:

txt = {t for t in files if t.lower().endswith((«.txt))}

Генераторы множеств позволяют использовать несколько циклов по разным итерируемым множествам с условными выражениями, создавать множества множеств или сделать множество фиксированным.

Как использовать генераторы в языке Python

Выражения-генераторы

Объявление генераторных выражений синтаксически похоже на создание словарей или списков, но, в отличие от списков, генераторы не хранят все значения выполняемой функции в памяти, а только последний элемент последовательности, условие перехода или прерывания.

Возьмем в качестве примера генераторное выражение, возводящее в куб числа от 1 до 5:

n = (i**3 for i in range(1, 6))

print(n)

>

<generator object <genexpr> at 0x7fb6dd886510>

При выводе переменной n на печать интерпретатор сообщает о том, что созданный объект — генератор. Для вычисления очередного значения используется метод next():

print(next(n))

>1

print(next(n))

>8

print(next(n))

>27

print(next(n))

>64

print(next(n))

>125

print(next(n))

>

StopIteration

Выполним то же самое с использованием цикла for:

n = (i**3 for i in range(1, 6))

for i in n:

print(i)

>

1

8

27

64

125

Метод next(n) последовательно рассчитывает и выводит значения:

1, 8, 27, 64, 125. При этом происходит стирание предыдущих значений из памяти. Во время 6-й итерации все значения будут удалены, поэтому интерпретатор вызовет исключение StopIteration.

Таким образом, генератор позволяет лишь единожды пройтись по элементам объекта и его повторное использование невозможно — нужно создавать новый.

Функции-генераторы

В предыдущем разделе мы рассмотрели генератор вида:

(выражение for item in итерируемый объект if условие).

Это выражение — частный случай использования функции-генератора. Генераторная функция определяется при помощи той же инструкции, что и обычная, — def. Отличие заключается в использовании yield, возвращающей объект-генератор, вместо инструкции return, сразу же возвращающей значение. Рассмотрим работу функции-генератора на примере чисел Фибоначчи:

def func(n):

fb1 = 0

fb2 = 1

for i in range(1,n):

yield fb2

fb_sum = fb1+fb2

fb1 = fb2

fb2 = fb_sum

fb = func(7)

print(fb)

for i in fb:

print(i)

>

<generator object func at 0x7f87e275e510>

1

1

2

3

5

8

Как видим, из консоли при вызове функции func(7) создается генератор fb.

При каждой новой итерации оператор yield приостанавливает выполнение следующих инструкций и возвращает значение fb2. Дальнейшие вызовы метода next(fb) приведут к тому, что выполнение функции продолжится с начала до следующего оператора yield. При этом, как можем заметить, значения промежуточных переменных fb1 и fb_sum фиксируются и изменяются в соответствии с кодом.

Функция-генератор поддерживает использование нескольких операторов yield. В этом случае возвратится значение справа от оператора, а код при последующем next() выполнится так же до следующего yield. Таким образом можно создавать сопрограммы-функции с возможностью приостанавливать и возобновлять выполнение программы в точках входа и выхода yield.

Рассмотрим еще один пример, где функция-генератор воспроизводит бесконечно длинную последовательность чисел.

def halfs(next_half = 0.0):

while True:

yield next_half

next_half += 0.5

Функция возвращает числа 0, 0.5, 1, 1.5 и т. д. до бесконечности.

Воспользуемся этой функцией для создания списка чисел, удовлетворяющих условию n < 3:

res = []

for i in halfs():

res. append(i)

if i >= 2.0:

break

print (res)

>

[0.0, 0.5, 1.0, 1.5, 2.0]

При каждом вызове функция halfs возвращает генератор, который увеличивает текущее значение на 0.5.

Значение можно не только вернуть, но еще и передать его в генератор с помощью метода send()

def triple_numbers():

while True:

n = yield

yield n * 3

g = triple_number()

next(g)

print(g.send(10))

next(g)

print(g.send(50))

>

10

50

При помощи метода throw() можно обрабатывать исключения:

def triple_numbers(num):

while True:

num *= 3

yield num

gen = triple_numbers(3)

for i in gen:

if i > 100:

gen.throw(Exception(«Error!»))

print(i)

>

9

27

81

Exception: Error!

Программа создает генератор, бесконечно умножающий пользовательское число на 3 до тех пор, пока оно не превысит 100, что вызовет исключение Error.

При помощи метода close() можно принудительно остановить выполнение функции генератора:

def triple_numbers(num):

while True:

num *= 3

yield num

gen = triple_numbers(3)

for i in gen:

if i > 100:

gen.close()

print(i)

>

9

27

81

243

Вызов метода close() генерирует исключение GeneratorExit в точке последнего вызова yield. Нет необходимости перехватывать это исключение, поскольку close() делает это автоматически. При этом сообщения об ошибке не возникает и последняя инструкция print(i) в цикле for успешно выполняется (на печать выводится число 243).

Как использовать генераторы в языке Python

Изучить применение генераторов на реальных практических задачах можно на курсах DevEducation. Программисты Python часто выбирают этот язык программирования в качестве своего первого, поскольку он отличается минималистичным и интуитивно понятным синтаксисом, простотой, многофункциональностью и универсальностью, востребованностью на рынке.

Освоение Python с нуля до уровня джуна при должном желании может занять не больше года. Курсы помогут существенно ускорить этот процесс и дать практический опыт в решении конкретных реальных задач, который невозможно найти на страницах справочников и самоучителей.

Присоединяйся к DevEducation — стань востребованным специалистом и построй карьеру в IT!