The Python Programming Guide

Итераторы

Итераторы - это объекты, которые содержат исчислимое количество значений. Другими словами, итерируя данный объект, вы перебираете его поэлементно по всем имеющимся значениям.

В Python, на техническом уровне, это реализовано следующим образом - итератором является объект, который выполняет итерационный протокол, т.е. содержит определённые методы __iter__() и __next__().

Итерируемые объекты

Итерируемыми объектами в Python, например из встроенных типов данных, являются:

Использование итератора

Получить итератор можно с помощью использования функции iter(), передав в качестве аргумента итерируемый объект:

numbers = [5, 7, 22, 3]
myiter = iter(numbers)

print(next(myiter))
print(next(myiter))
print(next(myiter))
5
7
22

Для получения следующего элемента из итератора используется функция next().

Использование итератора с циклом

Цикл for для итерируемых объектов неявно создает итератор, на который в последствии применяет функции next() при каждой итерации. Пример:

fruits = ["apple", "banana", "orange"]

for fruit in fruits:
    print(fruit)

Но можно и явно передать циклу for заранее полученный итератор:

fruits = ["apple", "banana", "orange"]
myiter = iter(fruits)

for fruit in myiter:
    print(fruit)

Создание итерируемого объекта

Как уже было сказано ранее, чтобы создать итерируемый объект, надо реализовать два специальных метода - __iter__() и __next__():

В качестве примера создадим объект, который будет возвращать числа от 0 с шагом 0.5:

class FloatNumbers:
    def __iter__(self):
        self.number = 0.0
        return self
    
    def __next__(self):
        current = self.number
        self.number += 0.5
        return current

fnums = FloatNumbers()
myiter = iter(fnums)

print(next(fnums))
print(next(fnums))
print(next(fnums))
0.0
0.5
1.0

StopIteration

В прошлом примере был создан итерируемый объект и он отлично работает с функцией next(), однако у него один недостаток. Если мы его передадим в цикл for, то он будет итерировать до бесконечности. Чтобы предотвратить данную ситуацию, можно использовать встроенное исключение StopIteration.

Добавим это исключение в метод __next__() в прошлый пример:

class FloatNumbers:
    def __iter__(self):
        self.number = 0.0
        return self
    
    def __next__(self):
        if self.number <= 3:
            current = self.number
            self.number += 0.5
            return current
        else:
            raise StopIteration

fnums = FloatNumbers()
myiter = iter(fnums)

for n in myiter:
    print(n)
0.0
0.5
1.0
1.5
2.0
2.5
3.0

Когда цикл for доходит до значения 3.0, вызывается исключение StopIteration и происходит выход из цикла.