Генераторы - где их найти: продолжение
Протокол итератора - это образ, которым объект должен вести себя в соответствии с правилами, налагаемыми контекстом инструкций for
и in
. Объект, соответствующий протоколу итератора, называется итератором.
Итератор должен предоставить два метода:
__iter__()
, который должен возвращать сам объект и который вызывается один раз (это необходимо для успешного запуска итерации в Python);__next__()
, который предназначен для возврата следующего значения (первого, второго и т. д.) нужной серии - он будет вызыватьсяfor
/in
, чтобы пройти следующую итерацию; если больше нет значений для предоставления, метод должен вызвать исключениеStopIteration
.
Звучит странно? Нет. Посмотрите на пример в редакторе.
Мы создали класс, способный перебирать значения первых n
(где n
- параметр конструктора) чисел Фибоначчи.
Напомним, что числа Фибоначчи (Fibi) определены следующим образом:
Fib1 = 1
Fib2 = 1
Fibi = Fibi-1 + Fibi-2
Другими словами:
- первые два числа Фибоначчи равны 1;
- любое другое число Фибоначчи является суммой двух предыдущих (например, Fib3 = 2, Fib4 = 3, Fib5 = 5 и т.д.).
Давайте углубимся в код:
- строки со 2 по 6: конструктор класса печатает сообщение (мы будем использовать это для отслеживания поведения класса), подготавливает некоторые переменные (
__n
для хранения ограничения серии,__i
для отслеживания текущего числа Фибоначчи, которое нужно предоставить, и__p1
вместе с__p2
для сохранения двух предыдущих чисел); - строки с 8 по 10: метод
__iter__
обязан возвращать сам объект итератора; его цель может быть немного двусмысленной, но в этом нет никакой тайны; попытайтесь представить объект, который не является итератором (например, это набор объектов), но один из его компонентов является итератором, способным сканировать наборы данных; метод__ter__
должен извлечь итератор и поручить ему выполнение протокола итерации; как видите, метод начинает свое действие с печати сообщения; - строки с 12 по 21: метод
__next__
отвечает за создание последовательности; это несколько многословно, но это должно сделать его более читабельным; сначала он печатает сообщение, затем обновляет количество требуемых значений, и если он достигает конца последовательности, метод прерывает итерацию, вызывая исключение StopIteration; остальная часть кода проста и точно отражает определение, которое мы показали вам ранее; - строки 23 и 24 используют итератор.
Код дает следующий вывод:
__init__
__iter__
__next__
1
__next__
1
__next__
2
__next__
3
__next__
5
__next__
8
__next__
13
__next__
21
__next__
34
__next__
55
__next__
output
Посмотрите:
- сначала создается объект итератора;
- затем Python вызывает метод
__iter__
, чтобы получить доступ к фактическому итератору; - метод
__next__
вызывается одиннадцать раз - первые десять раз дают полезные значения, а одиннадцатый завершает итерацию.
Code
class Fib:def __init__(self, nn):
print("__init__")
self.__n = nn
self.__i = 0
self.__p1 = self.__p2 = 1
def __iter__(self):
print("__iter__")
return self
def __next__(self):
print("__next__")
self.__i += 1
if self.__i > self.__n:
raise StopIteration
if self.__i in [1, 2]:
return 1
ret = self.__p1 + self.__p2
self.__p1, self.__p2 = self.__p2, ret
return ret
for i in Fib(10):
print(i)
{{ dockerServerErrorMsg }}
×
{{ errorMsg }}
×
{{ successMsg }}
×