На этом шаге мы рассмотрим общий способ их создания.
Вы хотите реализовать собственный паттерн итераций, который будет отличаться от обычных встроенных функций (таких как range(), reversed() и т. п.).
Если вы хотите реализовать новый тип итерационного паттерна, определите его с помощью генератора. Вот, например, генератор, который создает диапазон чисел с плавающей точкой:
>>> def frange(start, stop, increment): x = start while x < stop: yield x x += increment >>>
Чтобы использовать такую функцию, вы должны проитерировать по ней в цикле или применить ее с какой-то другой функцией, которая потребляет итерируемый объект (например, sum(), list() и т. п.). Например:
>>> for n in frange(0, 4, 0.5): print(n) 0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 >>> list(frange(0, 1, 0.125)) [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875] >>>
Само присутствие инструкции yield в функции превращает ее в генератор. В отличие от обычной функции, генератор запускается только в ответ на итерацию. Вот эксперимент, который вы можете провести, чтобы понять внутренний механизм работы таких функций:
>>> def countdown(n): print('Starting to count from', n) while n > 0: yield n n -= 1 print('Done!') >>> # Создает генератор - обратите внимание на отсутствие вывода >>> c = countdown(3) >>> c <generator object countdown at 0x0000020BE3E16740> >>> # Выполняется до первого yield и выдает значение >>> next(c) Starting to count from 3 3 >>> # Выполняется до следующего yield >>> next(c) 2 >>> # Выполняется до следующего yield >>> next(c) 1 >>> # Выполняется до следующего yield (итерирование останавливается) >>> next(c) Done! Traceback (most recent call last): . . . . next(c) StopIteration >>>
Ключевая особенность функции-генератора состоит в том, что она запускается только в ответ на операции next в ходе итерирования. Когда генератор возвращает значение, итерирование останавливается. Однако цикл for, который обычно используется для выполнения итераций, сам заботится об этих деталях, поэтому в большинстве случаев вам не стоит волноваться о них.
На следующем шаге мы рассмотрим реализацию протокола итератора.