Итераторы в Python: как они работают и как создать свои
Итераторы в Python
Узнайте, как функционируют итераторы Python, как обходить их с циклами for или функцией next(), и как создавать пользовательские или бесконечные итераторы с помощью протокола итератора.
Видео: Итераторы в Python
Итераторы в Python
Итераторы повсюду в Python. Они лежат в основе циклов for, генераторов списков, генераторов и многих встроенных структур данных, часто работая за кулисами.
Итератор — это просто объект, выдающий данные по одному элементу за раз. В Python любой объект, реализующий методы __iter__() и __next__(), считается итератором.
Когда объект может предоставить итератор, он называется итерируемым. Большинство встроенных контейнеров — списки, кортежи, строки, словари — являются итерируемыми. Встроенная функция iter() вызывает метод __iter__() объекта для получения итератора.
Итерация через итератор
Можно вручную продвигать итератор функцией next(). Когда итератор исчерпан, возбуждается StopIteration.
# определяем список
my_list = [4, 7, 0, 3]
# получаем итератор с помощью iter()
my_iter = iter(my_list)
# итерируем через next()
# Вывод: 4
print(next(my_iter))
# Вывод: 7
print(next(my_iter))
# next(obj) то же, что obj.__next__()
# Вывод: 0
print(my_iter.__next__())
# Вывод: 3
print(my_iter.__next__())
# Это вызовет ошибку, элементы закончились
next(my_iter)
Вывод
4 7 0 3 Traceback (most recent call last): File "<string>", line 24, in <module> next(my_iter) StopIteration
Более естественный способ итерации — цикл for, который автоматически управляет жизненным циклом итератора.
>>> for element in my_list:
... print(element)
...
4
7
0
3
Как цикл for работает с итераторами
Цикл for внутренне преобразуется в:
for element in iterable:
# сделать что-то с element
что эквивалентно следующей логике:
# создать объект итератора из итерируемого
iter_obj = iter(iterable)
# бесконечный цикл
while True:
try:
# получить следующий элемент
element = next(iter_obj)
# сделать что-то с элементом
except StopIteration:
# если StopIteration возбуждён, выйти из цикла
break
Это показывает, что цикл for по сути бесконечный цикл, полагающийся на протокол итератора и исключение StopIteration для завершения.
Создание пользовательских итераторов
Создание итератора с нуля просто: реализуйте __iter__() для возврата экземпляра итератора (часто self) и __next__() для выдачи следующего значения или возбуждения StopIteration при завершении.
Ниже — пример, выдающий последовательные степени двойки до заданного пользователем максимального показателя.
class PowTwo:
"""Итератор, выдающий степени двойки."""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
# создать объект
numbers = PowTwo(3)
# создать итерируемый из объекта
i = iter(numbers)
# Использование next для получения следующего элемента
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
Вывод
1 2 4 8 Traceback (most recent call last): File "/home/bsoyuj/Desktop/Untitled-1.py", line 32, in <module> print(next(i)) File "<string>", line 18, in __next__ raise StopIteration StopIteration
Использование цикла for с пользовательским итератором так же просто:
>>> for i in PowTwo(5):
... print(i)
...
1
2
4
8
16
32
Бесконечные итераторы в Python
Итераторы могут быть неограниченными. Встроенный iter() принимает вызываемый объект и значение-сентинел: он многократно вызывает функцию, пока возвращаемое значение не станет равно сентинелу. Например:
>>> int()
0
>>> inf = iter(int, 1)
>>> next(inf)
0
>>> next(inf)
0
Поскольку int() всегда возвращает 0, iter(int, 1) никогда не видит сентинел, производя бесконечный итератор.
Можно также создать свой бесконечный итератор, например, выдающий все нечётные числа:
class InfIter:
"""Бесконечный итератор, возвращающий последовательные нечётные числа."""
def __iter__(self):
self.num = 1
return self
def __next__(self):
num = self.num
self.num += 2
return num
Пример запуска:
>>> a = iter(InfIter())
>>> next(a)
1
>>> next(a)
3
>>> next(a)
5
>>> next(a)
7
При работе с бесконечными итераторами всегда предоставляйте условие завершения, чтобы избежать бесконечных циклов.
Итераторы эффективны по памяти, так как производят элементы по требованию. Теоретически можно генерировать неограниченную последовательность — вроде всех нечётных чисел — не храня весь набор.
Для более простого способа создания итераторов изучите генераторы Python с yield.
Python
- Освоение типов данных в Python: практическое руководство
- Мастерство операторов Python: Полное руководство
- Оператор pass в Python: практическое руководство
- Аргументы функций в Python: позиционные, именованные и параметры по умолчанию
- Словари в Python: создание, манипуляции и продвинутые техники
- Замыкания в Python: как вложенные функции захватывают переменные
- Модуль datetime в Python: практическое руководство по датам, времени и часовым поясам
- Освоение Python time.sleep(): лучшие практики, многопоточность и реальные примеры
- Обзор Python: высокоуровневый, читаемый, интерактивный и объектно-ориентированный язык
- Числа в Python: Неизменяемые числовые типы данных