Что происходит при использовании ключевого слова yield в генераторе Python
Что проверяют
Понимаете ли вы, что yield меняет не тело функции, а её природу: функция становится генераторной ещё на этапе компиляции. Дальше проверяют, представляете ли вы, когда именно выполняется код и что происходит с кадром между значениями.
Как отвечать
Если в теле есть yield или yield from, вызов функции не выполняет ни строки, а возвращает объект генератора. Тело начинает работать на первом next(), доходит до yield, отдаёт значение и замирает: кадр с локальными переменными и позицией сохраняется и живёт вместе с генератором.
def countdown(n):
while n > 0:
yield n
n -= 1Каждый следующий next() продолжает с места остановки. Когда тело заканчивается, поднимается StopIteration, а цикл for ловит её и просто завершается. return внутри генератора не возвращает значение вызывающему, он кладёт его в StopIteration.value, откуда значение достаёт yield from.
yield это выражение, а не только оператор: gen.send(x) возобновляет генератор и подставляет x результатом yield, gen.throw() бросает исключение в точке остановки, gen.close() бросает там GeneratorExit, и блоки finally отрабатывают.
Выгода в памяти и ленивости: элементы считаются по требованию, последовательность может быть бесконечной, конвейер генераторов не собирает промежуточные списки. Цена в том, что генератор одноразовый, не знает длины и не поддерживает индексацию.
Где спотыкаются
Думают, что вызов генераторной функции уже выполняет проверки аргументов. Проверка из первых строк сработает только при первом next(), поэтому валидацию выносят в обычную функцию-обёртку, которая внутри возвращает генератор.
Второе: пробуют пройти по генератору дважды. Второй цикл не выполнится ни разу, ошибки при этом не будет, данные просто исчезнут; для повторного прохода нужен list() или новый вызов функции.
Третье: возвращают наружу генератор, который читает файл внутри with. К моменту потребления блок уже закрыт, и вместо данных прилетает ошибка про операцию ввода-вывода на закрытом файле.