Создавать классы, чтобы прописать в них какую-то функциональность по перебору объектов, довольно интересно, но слишком многословно: нужно объявить класс как минимум с двумя методами, обычно с тремя.
Естественно, Python предлагает более простой способ реализовать функциональность классов-итераторов, если их единственной целью является перебор каких-то элементов. Таким способом являются генераторы.
<aside> 💡 Генераторы — функции, которые ведут себя как итераторы, то есть позволяют перебрать несколько значений.
</aside>
Основным отличием генератора от обычной функции является использование ключевого слова yield. Это ключевое слово позволяет вернуть значение, как и return, но при этом не завершает работу функции, а приостанавливает. При повторном вызове приостановленной функции она начнёт работу со строчки, следующей за yield.
Второе отличие — функция-генератор возвращает не какое-то конкретное значение, а итератор, по которому, например, можно пройтись с помощью функции next().
Перепишем пример с генерацией степеней двойки, используя генератор:
# Импортируем класс Iterator для аннотации
from collections.abc import Iterator
# Функция-генератор для степеней двойки
def pow_two(max_pow: int) -> Iterator:
cur_pow = 0
while cur_pow <= max_pow:
# Вот тут и происходит магия: когда функция
# будет вызвана повторно, она начнётся не с начала,
# а со строчки после yield
yield 2 ** cur_pow
cur_pow += 1
# Выводим степени двойки от 2^0 до 2^10
for i in pow_two(10):
print(i)
По сравнению с классом код стал меньше и лучше читается.
<aside> 👉 Если использовать генератор как объект, то по нему можно будет пройтись только один раз, для повторной итерации нужно будет создать объект заново.
</aside>
Это значит, что следующий код выведет степени двойки только один раз:
from collections.abc import Iterator
def pow_two(max_pow: int) -> Iterator:
cur_pow = 0
while cur_pow <= max_pow:
yield 2 ** cur_pow
cur_pow += 1
pt = pow_two(10)
# Здесь ещё есть, что перебирать
for i in pt:
print(i)
# А к этому моменту перебор уже закончился
for i in pt:
print(i)