我正在实现一个实用程序库,它是一种旨在在 Google App Engine 云计算服务的分布式环境中运行的任务管理器。 (它使用任务队列和内存缓存的组合来执行后台处理)。我计划使用生成器来控制任务的执行,本质上是通过在用户代码中使用 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 是双向通信 channel ,允许将值传递给使用生成器的代码。这允许模拟“抢占式 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 会将它 变成生成器函数。因此,上面的调用只会返回另一个生成器。只有在再次添加 .next() 和 yield 后,我们才会得到之前的结果:
yield self.sleep(timedelta(seconds=1)).next()
这当然比以前更加丑陋和不必要的冗长。
因此我的问题是:有没有一种方法可以将 yield 放入函数中,而不将其转换为生成器函数,而是让其他生成器可以使用它来产生由它计算的值?
请您参考如下方法:
你似乎错过了显而易见的东西:
class EntityWorker(Worker):
def setup(self):
self.entity_query = Entity.all()
def run(self):
for e in self.entity_query:
do_something_with(e)
yield self.sleep(timedelta(seconds=1))
def sleep(self, wait):
return Worker.SLEEP, wait
是yield将函数变成了生成器,不可能漏掉它。
要隐藏产量,您需要一个高阶函数,在您的示例中它是 map:
from itertools import imap
def slowmap(f, sleep, *iters):
for row in imap(f, self.entity_query):
yield Worker.SLEEP, wait
def run():
return slowmap(do_something_with,
(Worker.SLEEP, timedelta(seconds=1)),
self.entity_query)
