На этом шаге мы рассмотрим пример такого определения.
Вы хотите написать генератор, но функция работает с дополнительным состоянием, которое вам хотелось бы каким-то образом показать пользователю.
Если вам нужен генератор, который показывает пользователю дополнительное состояние, не забудьте, что вы можете легко реализовать его в форме класса, поместив код генератора в метод __iter__(). Например:
from collections import deque class linehistory: def __init__ (self, lines, histlen=3): self.lines = lines self.history = deque(maxlen=histlen) def __iter__(self): for lineno, line in enumerate(self.lines, 1): self.history.append((lineno, line)) yield line def clear(self): self.history.clear()
Вы можете обращаться с этим классом так же, как с обычным генератором. Однако, поскольку он создает экземпляр, вы можете обращаться к внутренним атрибутам, таким как history или метод clear(). Например:
with open('somefile.txt') as f: lines = linehistory(f) for line in lines: if 'python' in line: for lineno, hline in lines.history: print('{}:{}'.format(lineno, hline), end='')
С генераторами легко попасть в ловушку, если пытаться делать все только с помощью функций. В результате может получиться сложный код, если генератору нужно взаимодействовать с другими частями программы некими необычными способами (раскрытие атрибутов, разрешение на управление через вызов методов и т. п.). В этом случае просто используйте определение класса, как показано выше. Определение генератора в методе __iter__() не изменит ничего в том, как вы напишете алгоритм. Но тот факт, что генератор станет частью класса, упростит задачу предоставления пользователям атрибутов и методов для каких-то взаимодействий.
Потенциальная хрупкость показанного приема заключается в том, что он может потребовать дополнительного шага: вызова iter(), если вы собираетесь провести итерацию не через цикл for. Например:
>>> f = open('somefile.txt') >>> lines = linehistory(f) >>> next(lines) Traceback (most recent call last): . . . . TypeError: 'linehistory' object is not an iterator >>> # Сначала вызываем iter(), затем начинаем итерирование >>> it = iter(lines) >>> next(it) 'hello world\n' >>> next(it) 'this is a test\n' >>>
На следующем шаге мы рассмотрим получение среза итератора.