Треки
/
Python
Python
/
Салабус
/
Генератори
Ге

Генератори у Python

1 вправа

Про концепцію Генератори

generator - це функція або вираз, що повертає особливий тип ітератора, який називається генераторний ітератор. Generator-iterators є лінивими: вони не зберігають свої values у памʼяті, а генерують значення тоді, коли потрібно.

Функція-генератор має такий самий вигляд, як і будь-яка інша функція, але містить один або кілька виразів yield. Кожен yield зупиняє виконання коду, зберігаючи поточний стан виконання (разом з усіма локальними змінними та інструкціями try). Коли генератор відновлює роботу, він продовжує зі стану, збереженого під час зупинки, на відміну від звичайних функцій, які скидаються з кожним викликом.

Створення генератора

Генератори створюють майже так само, як інші функції з циклами чи рекурсією, але вони вимагають виразу yield, який ми докладніше розглянемо трохи згодом.

Приклад - функція, яка повертає квадрати із заданого масиву чисел.
У наведеному вигляді всі вхідні дані потрібно обробити, перш ніж можна буде повернути якісь значення:

>>> def squares(list_of_numbers):
...     squares = []
...     for number in list_of_numbers:
...         squares.append(number ** 2)
...     return squares

Перетворити цю функцію на генератор можна так:

>>> def squares_generator(list_of_numbers):
...     for number in list_of_numbers:
...         yield number ** 2

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

Це заощаджує памʼять і обчислювальні ресурси, адже обчислюється лише те значення, з яким ми працюємо зараз.

Використання генератора

Генератори (технічно generator-iteratorів, див. примітку нижче) - це різновид iterator, і їх можна використовувати будь-де в Python, де очікується iterator або iterable. Це стосується функцій або обʼєктів, які приймають iterable/iterator як аргумент. Щоб зануритися глибше, див. How to Make an Iterator in Python.

Note

Генераторні ітератори - це особливий підвид ітераторів. Iterators - це механізм/протокол, який уможливлює перебирання ітерабельних обʼєктів. Генераторні ітератори та ітератори, які повертають звичайні iterables у Python, поводяться дуже схоже, але є кілька важливих відмінностей, які варто зауважити:

  • Вони обчислюються ліниво; ітерація одностороння, і повернутися до попереднього значення не можна.
  • Їх споживають, перебираючи повернені значення; скидання чи збереження в памʼяті не існує.
  • Їх не можна впорядкувати й не можна розвернути.
  • Вони не є послідовностями й не мають indexes. Не можна звернутися до попереднього чи наступного значення за допомогою додавання чи віднімання, і не можна використовувати запис у квадратних дужках ([]) чи зрізи.
  • Їх не можна використовувати з функцією len(), бо вони не мають довжини.
  • Вони можуть бути скінченними або нескінченними: будьмо обережні, збираючи всі значення з нескінченного generator-iterator!

Щоб скористатися генератором squares_generator():

>>> squared_numbers = squares_generator([1, 2, 3, 4])

>>> for square in squared_numbers:
...     print(square)
...
1
4
9
16

Значення в генераторі також можна створювати й отримувати за допомогою функції next(). next() викликає метод __next__() обʼєкта-генератора, «просуваючи» або обчислюючи код генератора до його виразу yield, який потім «віддає» чи повертає значення.

>>> squared_numbers = squares_generator([1, 2])

>>> next(squared_numbers)
1
>>> next(squared_numbers)
4

Коли generator повністю вичерпано і він більше не має значень для повернення, він викидає помилку StopIteration.

>>> next(squared_numbers)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Різниця між ітерабельними обʼєктами та генераторами

Генератори - це особливий підвид ітераторів. Iterators - це механізм/протокол, який уможливлює перебирання ітерабельних обʼєктів. Генератори й ітератори, які повертають звичайні iterables у Python, поводяться дуже схоже, але є кілька важливих відмінностей, які варто зауважити:

  • Генератори односторонні: повернутися до попереднього значення не можна.

  • Перебирання генераторів споживає повернені значення; скидання немає.

  • Генератори (оскільки обчислюються ліниво) не можна впорядкувати й не можна розвернути.

  • Генератори не мають indexes, тож не можна звернутися до попереднього чи наступного значення за допомогою додавання чи віднімання.

  • Генератори не можна використовувати з функцією len().

  • Генератори можуть бути скінченними або нескінченними, тож будьмо обережні, збираючи всі значення з нескінченного генератора.

Вираз yield

Вираз yield дуже схожий на вираз return.

На відміну від виразу return, yield віддає значення тому, хто викликав, у певній точці, призупиняючи обчислення й повернення подальших значень, доки їх не запросять.

Коли yield обчислюється, він зупиняє виконання функції, у якій міститься, і повертає значення функції на той момент.

Після цього функція залишається в області видимості, і коли викликається __next__(), виконання відновлюється, доки знову не трапиться yield.

Note

Використовувати вирази yield поза функціями заборонено.

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

>>> lets_try = infinite_sequence()
>>> lets_try.__next__()
0
>>> lets_try.__next__()
1

Навіщо потрібні генератори?

Генератори стають у пригоді в багатьох задачах.

Працюючи з великою колекцією, ми можемо не хотіти тримати всі її значення в memory. Генератор дає змогу опрацьовувати великі дані частина за частиною, заощаджуючи памʼять і підвищуючи швидкодію.

Генератори також дуже допомагають, коли процес чи обчислення складні, дорогі або нескінченні:

>>> def infinite_sequence():
...     current_number = 0
...     while True:
...         yield current_number
...         current_number += 1

Тепер щоразу, коли для обʼєкта infinite_sequence викликають __next__(), він повертає попереднє число + 1.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Генератори

Практика заблокована

Розблокуйте ще 2 вправи, щоб практикувати концепцію Генератори