Итератор и итерируемый объект в Python — различия

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

Содержание

Итераторы

Iterator — это объект, реализующий протокол итератора. Другими словами, итератор — это объект, реализующий следующие методы:

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

Как только вы завершите итерацию коллекции с помощью итератора, он будет исчерпан. Это означает, что вы больше не можете использовать объект итератора.

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

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

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

Изучение встроенного списка и итератора списка

В Python список — это упорядоченная коллекция элементов. Это также итерируемый объект, поскольку объект списка имеет метод __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

Это связано с тем, что итератор списка исчерпан. Чтобы повторить список еще раз, вам нужно создать новый итератор.

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

Пример с iterable и iterator в Python

Ниже определяется класс Colors:

class Colors:
    def __init__(self):
        self.rgb = ['red', 'green', 'blue']
        self.__index = 0

    def __iter__(self):
        return self

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

        # return the next color
        color = self.rgb[self.__index]
        self.__index += 1
        return color

В этом примере класс Colors играет две роли: iterable и iterator.

Класс 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. Это неэффективно.

Сепарация итератора от итерируемого

Давайте отделим итератор цвета от его итератора, как это делает Python с итератором списка и списком.

Ниже определяется класс Colors:

class Colors:
    def __init__(self):
        self.rgb = ['red', 'green', 'blue']

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

Ниже определяется класс ColorIterator:

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

        # return the next color
        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 = ['red', 'green', 'blue']

    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, чтобы получить итератор, и использует этот итератор для перебора элементов объекта цветов.

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

class Colors:
    def __init__(self):
        self.rgb = ['red', 'green', 'blue']

    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

            # return the next color
            color = self.__colors.rgb[self.__index]
            self.__index += 1
            return color
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *