Как в генераторе Python принять значение, переданное извне?
Что проверяют
Знаете ли вы, что yield в Python не только оператор, но и выражение со значением, и понимаете ли протокол генератора целиком. Следом обычно спрашивают, почему первый send() должен быть с None и как yield from передаёт значения во вложенный генератор.
Как отвечать
Значение принимается выражением x = yield y. Генератор отдаёт наружу y и засыпает, а когда внешний код вызывает gen.send(v), выполнение продолжается, и x становится равным v. Дальше протокол:
send(value): возобновляет генератор и возвращает следующее значение, которое он выдаст черезyield. Если генератор завершился, бросаетStopIteration;next(gen): то же самое, чтоgen.send(None), поэтому в обычном циклеforвыражениеyieldвсегда даётNone;- запуск: только что созданный генератор ещё не дошёл до первого
yield, и отправить ему значение, отличное отNone, нельзя. Сначала вызываютnext(gen)илиgen.send(None); throw(exc): бросает исключение в точкеyield, генератор может его перехватить и продолжить;close(): бросает внутрьGeneratorExit, срабатываютfinallyи контекстные менеджеры;return value: значение уходит вStopIteration.value, аyield fromвозвращает его как результат выражения. Заодноyield fromпрозрачно пробрасываетsend()иthrow()во вложенный генератор.
Классический пример: скользящее среднее, где в цикле стоит value = yield average, и каждый send(число) сразу возвращает новое среднее.
На этом механизме строились первые корутины asyncio, декоратор @asyncio.coroutine удалён в Python 3.11. У асинхронных генераторов аналог send() называется asend().
Где спотыкаются
Отправляют значение сразу после создания генератора и получают TypeError: can't send non-None value to a just-started generator.
Не понимают, что возвращает send(). Результатом будет не принятое значение, а то, что генератор выдал на следующем yield, и если там yield без выражения, придёт None.
Путают send() с аргументами при создании. Параметры функции генератора задаются один раз, а send() нужен именно для обмена данными во время работы.