Итераторы и протокол итерации
Фундаментальный вопрос: что на самом деле делает for и чем итератор отличается от итерируемого.
Итерируемый (iterable) объект умеет отдать итератор через
__iter__. Итератор (iterator) умеет выдавать следующий элемент через__next__и заканчивается исключениемStopIteration.
Вопрос: как работает цикл for?
Чёткий ответ. for x in obj вызывает iter(obj), получает итератор и в цикле дёргает next(...), пока не прилетит StopIteration. Список — iterable, но не итератор: итератор для него создаётся отдельно.
nums = [10, 20, 30]
it = iter(nums) # получили итератор
print(next(it))
print(next(it))
print(next(it))
try:
next(it) # элементы кончились
except StopIteration:
print("StopIteration — конец")
Вывод:
10 20 30 StopIteration — конец
Свой итератор: реализуем протокол
Чтобы объект работал в for, достаточно реализовать __iter__ и __next__.
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self # сам объект — итератор
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for n in Countdown(3):
print(n)
Вывод:
3 2 1
Итератор одноразовый
Важная деталь: итератор «истощается». Пройдя по нему один раз, второй раз вы получите пустоту — в отличие от списка, по которому можно ходить много раз.
nums = [1, 2, 3]
it = iter(nums)
print("первый проход:", list(it))
print("второй проход:", list(it)) # уже пусто
print("список снова:", list(nums)) # а список — сколько угодно раз
Вывод:
первый проход: [1, 2, 3] второй проход: [] список снова: [1, 2, 3]
Итог
iterableотдаёт итератор через__iter__;iteratorвыдаёт элементы через__next__.for=iter()+ повторныйnext()доStopIteration.- Итератор одноразовый: после прохода он пуст.
Проверьте себя
1. Какие два метода образуют протокол итератора?
A__init__ и __call__
B__iter__ и __next__
C__len__ и __getitem__
D__enter__ и __exit__
2. Чем завершается итерация?
Areturn None
BИсключением StopIteration
CПустым списком
DБесконечным циклом
3. Что вернёт второй list(it) для итератора, по которому уже прошли?
AТе же элементы снова
BПустой список []
CОшибку
DNone