Я реализую библиотеку утилит, которая является своего рода диспетчером задач, предназначенным для работы в распределенной среде службы облачных вычислений Google App Engine. (Он использует комбинацию очередей задач и memcache для выполнения фоновой обработки). Я планирую использовать генераторы для управления выполнением задач, по сути, обеспечивая непревзойденный «параллелизм» с помощью yield
в коде пользователя.
Тривиальный пример - обработка группы объектов базы данных - может выглядеть примерно так:
class EntityWorker(Worker):
def setup():
self.entity_query = Entity.all()
def run():
for e in self.entity_query:
do_something_with(e)
yield
Как мы знаем, yield
- это двусторонний канал связи, позволяющий передавать значения в код, который использует генераторы. Это позволяет имитировать «вытесняющий API», такой как вызов SLEEP
ниже:
def run():
for e in self.entity_query:
do_something_with(e)
yield Worker.SLEEP, timedelta(seconds=1)
Но это безобразно. Было бы здорово спрятать yield
внутри отдельной функции, которая может быть вызвана простым способом:
self.sleep(timedelta(seconds=1))
Проблема заключается в том, что включение yield
в функцию sleep
превращает it в функцию генератора. Поэтому вызов выше просто вернет другой генератор. Только после добавления .next()
и yield
обратно мы получим предыдущий результат:
yield self.sleep(timedelta(seconds=1)).next()
что, конечно, еще более безобразно и излишне многословно, чем раньше.
Отсюда мой вопрос: есть ли способ поместить yield
в функцию, не превращая ее в функцию генератора, но используя ее другими генераторами для получения вычисляемых ею значений?