Module (3%)
Section (19%)

Генераторы - где их найти: продолжение

Протокол итератора - это образ, которым объект должен вести себя в соответствии с правилами, налагаемыми контекстом инструкций 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 }} ×