В программировании существует понятия подпрограмма и сопрограмма. Не говоря об асинхронном программировании, можно сказать, что подпрограмма это код, вызываемый из разных мест программы. Этот код возвращает результат в то место, из которого его вызвали. Программа его ждет. Сопрограмма также вызывается из программы, выполняет какие то действия, а потом возвращает промежуточный результат и ждет следующего вызова. При следующем вызове она продолжает с того места, где прервалась на прошлом вызове. Вызывающая программа также ждет сопрограмму.
В языке Python на идеях сопрограмм реализованы конструкции генераторов-итераторов. Эти функции при первом обращении организуют сопрограмму, а запускают обработку командой .next(). Возврат из сопрограммы промежуточных результатов выполняется оператором yield. Когда итерация закончена, сопрограмма выполняет оператор return. Построенная подобным способом сопрограмма может выполняться в циклах языка Python.
Подробнее этот алгоритм описан в PEP 255. Далее я привожу перевод части этого документа.
Когда функция генератор вызывается, актуальные аргументы как обычно связываются с именами формальных аргументов локальной функции, но код в теле этой функции не выполняется. Вместо этого возвращается объект типа генератор-итератор; это соответствует протоколу итератора, поэтому, в частности, может использоваться в циклах for естественным образом. Обратите внимание, что когда намерение ясно из контекста, безоговорочное имя «генератор» может использоваться для ссылки либо на функцию-генератор, либо на генератор-итератор.
Каждый раз, когда вызывается метод .next() генератора-итератора, начинает выполняться код в теле функции-генератора до тех пор пока не встретятся предложения yield или return, или пока не будет достигнут конеч тела процедуры.
Если встречается предложение yield, статус функции замораживается и значение expression_list возвращается вызвавшему .next(). Под "замораживанием" мы подразумеваем что сохраняется все локальное состояние, включая указатели на локальные переменные, указатели на инструкции, и внутренний стэк: сохраняется достаточно информации для того чтобы при следующем вызове .next(), функция могла бы действовать точно так же, как если бы оператор yield был просто еще одним внешним вызовом..
Ограничение: Предложение yield нельзя использовать в выражении try из конструкции try/finally. Сложность состоит в том, что нет никакой гарантии, что генератор когда-либо будет возобновлен, следовательно, нет никакой гарантии, что блок finally когда-либо будет выполнен.
Ограничение: генератор не может быть возобновлен во время его активной работы:
Когда встречается предложение return, управление происходит как и в любой другой функции. Сначала выполняются соответствующие завершающие выражения (finally clauses) (если они есть). Затем инициируется исклбчение StopIteration, сигнализируя что итератор закончен. Исключение StopIteration инициализируется и в случае если генератор закончил свою работу без явного предложения return.
Обратите внимание, что return не всегда эквивалентен инициализации StopIteration: разница заключается в том, как обрабатываются включающие конструкции try/except. Например,:
В языке Python на идеях сопрограмм реализованы конструкции генераторов-итераторов. Эти функции при первом обращении организуют сопрограмму, а запускают обработку командой .next(). Возврат из сопрограммы промежуточных результатов выполняется оператором yield. Когда итерация закончена, сопрограмма выполняет оператор return. Построенная подобным способом сопрограмма может выполняться в циклах языка Python.
Подробнее этот алгоритм описан в PEP 255. Далее я привожу перевод части этого документа.
Определение: Yield
Вводится новое предложение:yield_stmt: "yield" expression_listПредложение yield можно использовать только внутри функций. Функция, содержащая предложение yield называется функцией генератором. Функция генератор во всех отношениях является обычной функцией, но в члене кодового объекта co_flags этой функции, установлен новый флаг CO_GENERATOR.
Когда функция генератор вызывается, актуальные аргументы как обычно связываются с именами формальных аргументов локальной функции, но код в теле этой функции не выполняется. Вместо этого возвращается объект типа генератор-итератор; это соответствует протоколу итератора, поэтому, в частности, может использоваться в циклах for естественным образом. Обратите внимание, что когда намерение ясно из контекста, безоговорочное имя «генератор» может использоваться для ссылки либо на функцию-генератор, либо на генератор-итератор.
Каждый раз, когда вызывается метод .next() генератора-итератора, начинает выполняться код в теле функции-генератора до тех пор пока не встретятся предложения yield или return, или пока не будет достигнут конеч тела процедуры.
Если встречается предложение yield, статус функции замораживается и значение expression_list возвращается вызвавшему .next(). Под "замораживанием" мы подразумеваем что сохраняется все локальное состояние, включая указатели на локальные переменные, указатели на инструкции, и внутренний стэк: сохраняется достаточно информации для того чтобы при следующем вызове .next(), функция могла бы действовать точно так же, как если бы оператор yield был просто еще одним внешним вызовом..
Ограничение: Предложение yield нельзя использовать в выражении try из конструкции try/finally. Сложность состоит в том, что нет никакой гарантии, что генератор когда-либо будет возобновлен, следовательно, нет никакой гарантии, что блок finally когда-либо будет выполнен.
Ограничение: генератор не может быть возобновлен во время его активной работы:
>>> def g(): ... i = me.next() ... yield i >>> me = g() >>> me.next() Traceback (most recent call last): ... File "", line 2, in g ValueError: generator already executing
Определение: Return
Функция-генератор может содержать предложения возврата в виде:returnОбратите внимание на то, что список-выражений(expression_list) нельзя указывать в предложениях return в теле генераторов (хотя, конечно, их можно указывать в теле функций не-генераторов, которые находятся внутри функций-генераторов).
Когда встречается предложение return, управление происходит как и в любой другой функции. Сначала выполняются соответствующие завершающие выражения (finally clauses) (если они есть). Затем инициируется исклбчение StopIteration, сигнализируя что итератор закончен. Исключение StopIteration инициализируется и в случае если генератор закончил свою работу без явного предложения return.
Обратите внимание, что return не всегда эквивалентен инициализации StopIteration: разница заключается в том, как обрабатываются включающие конструкции try/except. Например,:
>>> def f1(): ... try: ... return ... except: ... yield 1 >>> print list(f1()) []потому, что здесь как в любой функции return это просто возврат, но:
>>> def f2(): ... try: ... raise StopIteration ... except: ... yield 42 >>> print list(f2()) [42]А здесь исключение StopIteration как любое исключение заставляет выполниться выражение в правиле except.