Что произойдет при повторном вызове функции iter на итераторе в Python
Что проверяют
Различаете ли вы итерируемый объект и итератор и знаете ли, что делает for под капотом. Вопрос кажется мелочью, но на нём видно, читал ли кандидат протокол итерации или запомнил рецепты.
Как отвечать
iter(x) вызывает x.__iter__(). У итератора этот метод возвращает сам объект, поэтому повторный вызов отдаёт тот же итератор: новый не создаётся и позиция не сбрасывается.
it = iter([1, 2, 3])
next(it) # 1
it2 = iter(it)
it2 is it # True
next(it2) # 2, продолжает с той же позицииЭто не случайность, а требование протокола, и держится на нём удобная вещь: в for можно передать и коллекцию, и уже начатый итератор, во втором случае цикл дочитает остаток. Отсюда типовой приём с заголовком файла:
rows = iter(reader)
header = next(rows)
for row in rows: # заголовок уже съеден
...Список ведёт себя иначе, потому что он итерируемый объект, а не итератор: каждый iter создаёт новый итератор, и ходить по списку можно сколько угодно раз. Если нужен повторный проход по итератору, вариантов два: получить свежий итератор из исходного источника или материализовать данные в список. itertools.tee буферизует уже прочитанное и источник не перематывает. Полезно упомянуть и вторую форму: iter(callable, sentinel) создаёт новый итератор, который зовёт функцию, пока та не вернёт сторожевое значение.
Где спотыкаются
Ждут перемотки: «позову iter ещё раз и начну сначала». С генератором это особенно неприятно, потому что второй цикл не падает, а просто не выполняет ни одной итерации, и пустой результат списывают на фильтр или на пустые данные.
Пишут свой класс, где __iter__ у итератора возвращает новый объект или объект с побочным эффектом. Правило простое: итерируемый объект отдаёт свежий итератор, итератор отдаёт себя. При его нарушении ломаются вложенные циклы и любая функция, которая внутри ещё раз зовёт iter.
Забывают, что одноразовость касается не только своих классов. zip, map, enumerate и генераторные выражения тоже исчерпываются, а проверка вида if x in it молча съедает часть элементов, и дальнейший цикл идёт по остатку.