Итераторы
Представьте, что вы читаете книгу по вечерам. Закончив, вы кладёте между страницами закладку. На следующий вечер вы открываете книгу на закладке и читаете дальше, а не с первой страницы.
Цикл for обходит списки, строки, словари, range и файлы, и у всех них разное устройство. Брать значения по одному ему помогает итератор — объект, который помнит, на каком месте остановился обход. Итератор похож на закладку: сама книга — это список, а закладка хранит только место, где вы остановились.
Итератор даёт функция iter, а следующее значение — функция next:
nums = [10, 20]
it = iter(nums)
print(next(it)) # 10
print(next(it)) # 20
print(next(it, "end")) # endДавайте разберём пример по строкам:
iter(nums)кладёт закладку перед первым элементом списка и возвращает итераторit.- Первый
next(it)возвращает10и сдвигает закладку на один элемент. - Второй
next(it)возвращает20: закладка помнит, где остановилась. - Элементы кончились, и
next(it, "end")возвращает свой второй аргумент.
Без второго аргумента next(it) на исчерпанном итераторе вызывает исключение StopIteration. Назад итератор не ходит и своей длины не знает: у него есть только следующий элемент. В этом аналогия и заканчивается: бумажную закладку можно переложить на начало, а итератор умеет двигаться только вперёд.
Что делает for
Цикл for ch in "ab": выполняет те же шаги, что и эта программа:
it = iter("ab")
while True:
try:
ch = next(it)
except StopIteration:
break
print(ch, end=".") # a.b.Разберём, что здесь делает цикл for:
- Он вызывает
iterодин раз, перед первой итерацией. - Перед каждой итерацией он вызывает
nextи записывает результат в переменную цикла. - Когда
nextвызываетStopIteration, цикл перехватывает исключение и завершается.
StopIteration для цикла не ошибка, а сигнал конца. Итератор словаря выдаёт ключи, поэтому for k in d перебирает ключи словаря d.
Итерируемый объект и итератор
Этот вопрос часто задают на собеседовании: «Чем итерируемый объект отличается от итератора?» Разберём оба понятия.
Итерируемый объект — книга
Итерируемый объект — всё, из чего iter умеет сделать итератор: список, строка, словарь, range. Это книга. Её можно читать сколько угодно раз, потому что каждый вызов iter кладёт новую закладку в начало: iter(nums) is iter(nums) даёт False.
Итератор — закладка
Итератор — это сама закладка, и проходится он один раз: исчерпанный, он остаётся пустым. zip и enumerate возвращают итераторы, а не списки, и файловый объект — тоже итератор. Возьмём pairs = zip("ab", [1, 2]):
- первый
list(pairs)даёт[('a', 1), ('b', 2)]; - второй
list(pairs)даёт[], потому что закладка уже в конце.
Итератор тоже итерируемый: iter(pairs) возвращает сам pairs. Поэтому for по итератору продолжает с того места, где обход остановился.
Частая ошибка
next вызывают прямо от списка:
nums = [10, 20]
print(next(nums)) # ✗TypeError: 'list' object is not an iteratorСписок — итерируемый объект, но не итератор: позиции обхода он не хранит. Тот же TypeError дают строка, словарь и range: все они итерируемые объекты, но не итераторы. Сначала получите итератор: next(iter(nums)) даёт 10.
Главное
iterдаёт итератор, аnext— его следующий элемент; в концеnextвызываетStopIteration.next(it, default)вместо исключения возвращаетdefault.forсам вызываетiterиnextи завершается наStopIteration.- Итерируемый объект можно обходить много раз, а итератор — только один.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.