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

В этой статье вы узнаете, в чем отличия итераторов и итерируемых объектов в Python.

Итераторы

Итератор — это объект, с помощью которого можно перебирать коллекции. Для этого у итератора должны быть следующие методы:

  • __iter__ — возвращает сам объект итератора.
  • __next__ — возвращает следующий элемент.

Когда вы завершаете перебор коллекции с помощью итератора, он «исчерпывается». Это означает, что больше тот итератор использовать нельзя.

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

Итерируемый объект — это объект, который можно перебирать. 

Объект является итерируемым, если он реализует метод __iter__, который возвращает новый итератор.

Встроенные списки и итератор списков

Cписок — это упорядоченная коллекция элементов. Он является итерируемым объектов, поскольку у него есть метод __iter__, который возвращает итератор. Например:

numbers = [1, 2, 3]

number_iterator = numbers.__iter__()
print(type(number_iterator))

Вывод

<class 'list_iterator'>

В этом примере метод __iter__ возвращает итератор типа list_iterator.

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

numbers = [1, 2, 3]
number_iterator = iter(numbers)

Поскольку у list_iterator также есть метод __next__, вы можете использовать встроенную функцию next() для перебора списка:

numbers = [1, 2, 3]

number_iterator = iter(numbers)

next(number_iterator)
next(number_iterator)
next(number_iterator)

Если вызвать эту функцию еще раз, вы получите ошибку StopIteration.

next(number_iterator) # ошибка StopIteration

Так происходит потому, что итератор списка был исчерпан. Чтобы повторить итерацию списка, необходимо создать новый итератор.

Это иллюстрирует разницу между списком и его итератором. Список создается один раз, а итератор создается каждый раз, когда вам нужно выполнить перебор списка.

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

Давайте создадим класс Colors:

class Colors:
    def __init__(self):
        self.rgb = ['красный', 'зеленый', 'синий']
        self.__index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.__index &gt;= len(self.rgb):
            raise StopIteration

        # возвращаем следующий цвет
        color = self.rgb[self.__index]
        self.__index += 1
        return color

В этом примере класс Colors играет две роли. Он одновременно является итератором и итерируемым объектом.

Класс Colors является итератором, поскольку в нем реализованы методы __iter__ и __next__. Метод __iter__ возвращает сам объект, а __next__ возвращает следующий элемент из списка.

Класс Colors является итерабельным, поскольку реализует метод __iter__, который возвращает сам объект, являющийся итератором.

Давайте создадим новый экземпляр класса Colors и выполним перебор его элементов с помощью цикла for:

colors = Colors()

for color in colors:
    print(color)

После завершения перебора объект colors становится бесполезным. Если вы попытаетесь повторить итерацию, вы получите исключение StopIteration:

next(colors) # ошибка StopIteration

Если вы по новой запустите цикл for, вы ничего не получите, потому что итератор исчерпан:

for color in colors:
    print(color)

Чтобы повторить перебор, необходимо создать новый объект colors с атрибутом rgb. Но это неэффективно.

Различаем итераторы и итерируемые объекты

Давайте отделим итератор color от итерабельного объекта color, как это делает Python с итератором списка и списком.

Создадим класс Colors:

class Colors:
    def __init__(self):
        self.rgb = ['красный', 'зеленый', 'синий']

    def __len__(self):
        return len(self.rgb)

Теперь создадим класс ColorsIterator:

class ColorIterator:
    def __init__(self, colors):
        self.__colors = colors
        self.__index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.__index >= len(self.__colors):
            raise StopIteration

        # возвращаем следующий цвет
        color = self.__colors.rgb[self.__index]
        self.__index += 1
        return color

Как это работает

  • Метод __init__ принимает итератор, который является экземпляром класса Colors.
  • Метод __iter__ возвращает сам итератор.
  • Метод __next__ возвращает следующий элемент из объекта Colors.

Теперь покажем, как использовать ColorIterator для перебора объекта Colors:

colors = Colors()
color_iterator = ColorIterator(colors)

for color in color_iterator:
    print(color)

Для повторного перебора объекта Colors достаточно создать новый экземпляр ColorIterator.

Но есть одна проблема!

Когда вы хотите перебрать объект Colors, вам нужно вручную создать новый объект ColorIterator. При этом нужно запомнить имя итератора ColorIterator.

Было бы здорово автоматизировать этот процесс. Для этого можно сделать класс Colors итерируемым объектом, реализовав метод __iter__. Давайте это и сделаем:

class Colors:
    def __init__(self):
        self.rgb = ['красный', 'зеленый', 'синий']

    def __len__(self):
        return len(self.rgb)

    def __iter__(self):
        return ColorIterator(self)

Метод __iter__ возвращает новый экземпляр класса ColorIterator.

Теперь вы можете выполнять перебор объекта Colors без явного создания объекта ColorIterator:

colors = Colors()

for color in colors:
    print(color)

Внутри цикла for вызывается метод __iter__ объекта colors для получения итератора. Он используется для перебора элементов объекта colors.

Давайте поместим класс ColorIterator в класса Colors, чтобы инкапсулировать их в один класс:

class Colors:
    def __init__(self):
        self.rgb = ['красный', 'зеленый', 'синий']

    def __len__(self):
        return len(self.rgb)

    def __iter__(self):
        return self.ColorIterator(self)

    class ColorIterator:
        def __init__(self, colors):
            self.__colors = colors
            self.__index = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self.__index >= len(self.__colors):
                raise StopIteration

            # возвращаем следующий цвет
            color = self.__colors.rgb[self.__index]
            self.__index += 1
            return color

Что нужно запомнить

  • Итерируемый объект — это объект с методом __iter__, который возвращает итератор.
  • Итератор — это объект с методом __iter__, который возвращает самого себя, и методом __next__, который возвращает следующий элемент.
  • Итераторы также являются итерируемыми объектами. Однако они «исчерпываются», а итерируемые объекты — нет.
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©