Итераторы и итерируемые объекты в 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 >= 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__
, который возвращает следующий элемент. - Итераторы также являются итерируемыми объектами. Однако они «исчерпываются», а итерируемые объекты — нет.