Редактировать: я рекомендую использовать Гринлет .Но если вы заинтересованы в чистом подходе Python, читайте дальше.
Этот вопрос рассматривается в PEP 342 , но сначала это довольно сложно понять.Я постараюсь просто объяснить, как это работает.
Для начала позвольте мне подвести итог тому, что я считаю проблемой, которую вы действительно пытаетесь решить.
Проблема
У вас есть стек вызовов функций генератора, вызывающих другие функции генератора.То, что вы действительно хотите - это иметь возможность получать доход от генератора наверху, и иметь выход, распространяющийся по всему стеку.
Проблема в том, что Python не ( на уровне языка) поддерживает реальные сопрограммы, только генераторы.(Но они могут быть реализованы.) Реальные сопрограммы позволяют вам останавливать весь стек вызовов функций и переключаться на другой стек.Генераторы позволяют вам остановить только одну функцию.Если генератор f () хочет выдать, оператор yield должен быть в f (), а не в другой функции, которую вызывает f ().
Решение, которое, я думаю, вы используете сейчас, состоит в том, чтобысделайте что-то подобное в ответе Саймона Стеллинга (то есть вызовите f () для вызова g (), получив все результаты g ()).Это очень многословно и безобразно, и вы ищете синтаксический сахар, чтобы обернуть этот шаблон.Обратите внимание, что это по существу разматывает стек каждый раз, когда вы сдаете, а затем снова заводите его обратно.
Решение
Существует лучший способ решения этой проблемы.Вы в основном реализуете сопрограммы, запуская свои генераторы поверх системы «батут».
Чтобы это работало, вам нужно следовать паре паттернов: 1. Когда вы хотите вызвать другую сопрограмму, выведите ее.2. Вместо того, чтобы возвращать значение, выведите его.
, поэтому
def f():
result = g()
# …
return return_value
становится
def f():
result = yield g()
# …
yield return_value
Скажем, вы в f ().Батутная система называется F ().Когда вы даете генератор (скажем, g ()), батутная система вызывает g () от вашего имени.Затем, когда g () закончила давать значения, батутная система перезапускает f ().Это означает, что вы на самом деле не используете стек Python;вместо этого система батута управляет стеком вызовов.
Когда вы получаете что-то, кроме генератора, система батута обрабатывает это как возвращаемое значение.Он передает это значение обратно генератору вызывающей стороны через оператор yield (используя метод генераторов .send ()).
Комментарии
Этот тип системы чрезвычайно важен и полезен в асинхронных приложениях,как те, которые используют Торнадо или Twisted.Вы можете остановить весь стек вызовов, когда он заблокирован, пойти и сделать что-то еще, а затем вернуться и продолжить выполнение первого вызова, где он остановился.
Недостаток вышеприведенного решения заключается в том, что оно требует от вас записипо сути все ваши функции как генераторы.Возможно, лучше использовать реализацию истинных сопрограмм для Python - см. Ниже.
Альтернативы
Существует несколько реализаций сопрограмм для Python, см .: http://en.wikipedia.org/wiki/Coroutine#Implementations_for_Python
Гринлет - отличный выбор.Это модуль Python, который изменяет интерпретатор CPython, чтобы разрешить истинные сопрограммы путем замены стека вызовов.
Python 3.3 должен предоставлять синтаксис для делегирования подгруппе, см. PEP 380 .