Классы-иттераторы

В Python 2.2 появился специальный интерфейс для итераторов, благодаря чему итераторы стало возможным описывать единообразно. Тот же пример с использованием стандартного интерфейса:

class Fibonacci:

  """Итератор последовательности Фибоначчи"""

  def __init__(self, max):

    self.n, self.a, self.b, self.max = 0, 0, 1, max

  def __iter__(self):

    return self

  def next(self):

    if self.n < self.max:

      a, self.n, self.a, self.b = self.a, self.n+1, self.b, self.a+self.b

      return a

    else:

      raise StopIteration

Заметьте, что по сравнению с предыдущим примером произошли три изменения. Во-первых, добавился метод __iter__(), который возвращает объект-итератор (в нашем примере -- сам объект). Во вторых, вместо метода __getitem__(), через который цикл for получал значения последовательности, применен метод next(), который является необходимым для объекта-итератора в Python. В-третьих, теперь в качестве сигнала завершения итераций используется исключение StopIteration.

В Python также появилась встроенная функция iter(), которая создает итератор для некоторой последовательности. Пример:

s = [1, 2, 3, 4, 5]

siter = iter(s)

for i in siter:

    print i

Конечно, в данном случае итератор нужен не был, так как оператор for прекрасно работает с последовательностями.

Есть и другая форма функции iter(), которая в качестве первого аргумента принимает функцию без аргументов, а в качестве второго аргумента -- стоповое значение.

В следующем примере происходит ввод и суммирование чисел. Итерации останавливаются при вводе числа 0:

s = 0

for i in iter(input, 0):

    s += i

print s

Кстати, построчное чтение из файла можно теперь записать так:

f = open("file.txt")

for l in iter(f.readline, ""):

    print l  # делаем что-то с очередной строкой файла

Итераторы применяются в нескольких стандартных модулях Python. Например, с помощью finditer() из модуля re легко получить в обычном цикле for все куски строки, соответствующие регулярному выражению:

for i in re.finditer("[0-9]+", "12 3 1 23 fff 1678"):

   print int(i.group())