Ітератор – це об’єкт, який у собі реалізує інтерфейс перебору чогось (іншими словами описує правило, за яким ми будемо перебирати вміст того чи іншого контейнера).

Наприклад в мене є список, який містить імена:

names = ['Костянтин', 'Ярослав', 'Остап']

for name in names:
    print(name)

При спробі перебрати список циклом for, відбувається взаємодія з ітератором списку, котрий цикл for отримує за рахунок неявного виклику вбудованої функції iter().

Функція iter в якості аргументу приймає об’єкт, що ітерується. Якщо в результаті її виклику буде отримано об’єкт ітератор, подальша робота проводитиметься вже з ним.

Для прикладу можна подивитись, що буде отримано, якщо передати мій список у метод iter:

print(iter(names))
# <list_iterator object at 0x000001B2C3E672B0>

В результаті отримано об’єкт типу list_iterator, який інкапсулює в собі правила перебору списку.

Після отримання ітератора цикл for, за допомогою ще однієї вбудованої функції next (котрій в якості аргументу передає отриманий ітератор), робить запит наступного елемента списку до тих пір, поки не отримає у відповідь повідомлення, що елементи закінчились (помилка StopIteration).

Якщо спробувати написати аналог циклу for, за допомогою конструкції while:

names = ['Костянтин', 'Ярослав', 'Остап']
iterator = iter(names)

try:
    while True:
        name = next(iterator)
        print(name)
except StopIteration:
    print("Ітерацію завершено")

Створення власних ітераторів

Створені нами класи за замовчуванням не підлягають ітерації. Для створення класу котрий можна ітерувати, потрібно реалізувати в ньому методи __iter__() та __next__():

  • __iter__() – повертає сам об’єкт ітератора
  • __next__() – повертає наступний елемент. Після досягнення кінця та під час наступних викликів, він повинен викликати StopIteration
class Numbers:

    def __init__(self, max_number):
        self.current = 1
        self.max_number = max_number

    def __iter__(self):
        return self

    def __next__(self):
        if self.current <= self.max_number:
            result = self.current ** 2
            self.current += 1
            return result
        else:
            raise StopIteration

Імпровізований клас Numbers, в кожній ітерації повертає число в другій ступені, до тих пір, поки не досягне обмеження max_number, встановленого під час створення об’єкту.

Зверніть увагу, що в моєму випадку, об’єкт мого класу є ітератор сам по собі, тому __iter__() повертає self.

Спробую зробити ітерацію по класу Numbers циклом for:

for number in Numbers(4):
    print(number)
    
# Output
# 1
# 4
# 9
# 16

Висновок

  • об’єкт що ітерується – це об’єкт який можна перебирати
  • за правило перебору відповідає ітератор, а не сам об’єкт
  • об’єкт що ітерується, при спробі його перебрати, повинен вміти повертати свій ітератор, щоб вже з ним тривала робота

Мітки: