Что делает двухаргументная форма встроенной функции iter в Python?

Что делает двухаргументная форма встроенной функции iter в Python?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Двухаргументная форма iter создаёт итератор, который при каждом обращении вызывает переданную функцию без аргументов и возвращает её результат, пока тот не станет равен ограничителю. Сам ограничитель в последовательность не попадает; после его получения итератор завершается.

Исторический контекст

Такой подход предназначен для представления потокового источника как обычного итератора. Он решает типичную задачу чтения порциями: вместо ручного цикла с вызовом функции, проверкой результата и остановкой можно использовать стандартный протокол обхода Python.

Механизм особенно полезен для источников, у которых нет естественной коллекции целиком: файлов, сокетов, потоковых декодеров и функций чтения блоков.

Постановка проблемы

Источник может возвращать данные порциями, а специальное значение — например пустую строку или пустые байты — означает конец ввода. Если собрать все данные заранее, возрастут потребление памяти и задержка до начала обработки.

Ручная реализация цикла обычно повторяет одну и ту же логику: вызвать функцию, проверить признак завершения, обработать значение. Ошибка в порядке проверки может привести к тому, что ограничитель будет ошибочно обработан как обычный элемент.

Подробное решение

Вызов iter(callable, sentinel) возвращает итератор. При каждом вызове next он вызывает callable() без аргументов, сравнивает результат с sentinel и выдаёт результат, только если они не равны. При совпадении итератор немедленно завершает обход и последующие вызовы next приводят к StopIteration.

from io import StringIO source = StringIO("abcdef") chunks = iter(lambda: source.read(2), "") print(list(chunks)) # ['ab', 'cd', 'ef']

Функция чтения вызывается до тех пор, пока не вернёт пустую строку. Пустая строка является маркером конца и не включается в результат.

Сравнение выполняется по равенству, а не обязательно по идентичности объектов. Поэтому ограничитель должен быть выбран так, чтобы обычный результат функции не считался ему равным. Исключение, возникшее внутри функции, не превращается автоматически в конец итерации — оно передаётся вызывающему коду.

Итератор является одноразовым: после завершения его нельзя «перемотать», не создав новый объект или не восстановив состояние источника. Функция также должна быть вызываемой без аргументов; если ей нужны параметры, их обычно связывают через замыкание или functools.partial.

Ситуация из практики

Сервис читает большой файл блоками по 8192 байта и сразу передаёт блоки в обработчик. Рассматривались три варианта:

  • Полная загрузка файла проста, но может привести к высокому потреблению памяти и не позволяет начать обработку до окончания чтения.
  • Ручной цикл с while гибок и удобен, если требуется сложная логика завершения, но содержит больше повторяющегося кода и повышает риск ошибочно обработать пустой блок.
  • Двухаргументный iter компактно выражает правило «читать до пустого результата» и совместим с for, list, map и другими потребителями итераторов.

Для обычного последовательного чтения выбран третий вариант: данные обрабатываются лениво, в памяти находится только текущий блок, а маркер конца не попадает в обработчик. Если же источник может возвращать пустой блок как допустимые данные, такой ограничитель применять нельзя без отдельного протокола завершения.

Что кандидаты часто упускают

  1. Можно ли передать в двухаргументный iter функцию с параметром?

Нет, непосредственно вызываемый объект должен поддерживать вызов без аргументов. Передача функции, требующей обязательный параметр, приведёт к ошибке при первом получении элемента. Параметры нужно заранее связать, например с помощью замыкания или functools.partial.

  1. Будет ли ограничитель возвращён как последний элемент?

Нет. Результат функции сначала сравнивается с ограничителем, и при равенстве итератор завершается без выдачи этого значения. Это отличается от обычного фильтрования: ограничитель не «отбрасывается после выдачи», а вообще не становится элементом последовательности.

  1. Что произойдёт, если функция-источник выбросит исключение до достижения ограничителя?

Исключение будет передано наружу и прервёт текущий обход. Двухаргументный iter обрабатывает только условие остановки по равенству с ограничителем; он не подавляет IOError, ValueError или другие исключения. Если источник может завершаться ошибкой, её следует обрабатывать на уровне вызывающего кода или оборачивать функцию-источник отдельной логикой.