Большинство объектов - контейнеров можно зациклить, используя инструкцию for ... in
:
for element in [1, 2, 3]: print(element) for element in (1, 2, 3): print(element) for key in {'one':1, 'two':2}: print(key) for char in '123': print(char) for line in open('myfile.txt'): print(line, end='')
Этот стиль доступа понятен, лаконичен и удобен. Использование итераторов пронизывает и объединяет Python. За кулисами оператор for
вызывает функцию iter()
для объекта контейнера. Функция возвращает объект итератора, который определяет метод __next__()
, который, в свою очередь обращается к элементам в контейнере по одному за раз. Когда нет больше элементов, __next__()
возбуждает исключение StopIteration
, которое указывает циклу о завершении.
Простой итератор можно создать, применив к последовательности встроенную функцию iter(s)
. Что бы получить элемент итератора, необходимо вызвать метод __next__()
с помощью встроенной функции next()
.
Пример показывает, как все это работает:
>>> s = 'abc' >>> it = iter(s) >>> it # <iterator object at 0x00A1DB50> >>> next(it) # 'a' >>> next(it) # 'b' >>> next(it) # 'c' >>> next(it) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # next(it) # StopIteration
Обратите внимание, что после использования итератор it
остался пустым и его невозможно больше использовать. Итераторы не поддерживают извлечение срезов и получение элемента по индексу. К ним так же нельзя применить функцию len()
(узнать длину итерации).
Посмотрев на механику протокола итератора, легко добавить поведение итератора в классы. Для этого, определите метод __iter__()
, который возвращает объект с методом __next__()
. Если класс определяет метод __next__()
, то __iter__()
может просто вернуть self
:
class Reverse: """Итератор для циклического перебора последовательности в обратном направлении. """ def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index]
>>> rev = Reverse('spam') >>> iter(rev) #<__main__.Reverse object at 0x00A1DB50> >>> for char in rev: ... print(char) ... # m # a # p # s