我正在实现一个实用程序库,它是一种旨在在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
是双向通信通道,允许将值传递给使用生成器的代码。这允许模拟“抢占式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
置于函数中而不将其转换为生成函数,但是让其他生成器可以使用它来生成由它计算的值?
答案 0 :(得分:3)
你似乎错过了显而易见的事情:
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)
答案 1 :(得分:2)
def sleepjob(*a, **k):
if a:
return Worker.SLEEP, a[0]
else:
return Worker.SLEEP, timedelta(**k)
所以
yield self.sleepjob(timedelta(seconds=1))
yield self.sleepjob(seconds=1)
对我来说没问题。
答案 2 :(得分:1)
我建议您查看ndb。它使用生成器作为协同例程(正如您在此处提出的那样),允许您编写异步处理rpcs的程序。
api通过使用另一个“填充”生成器的函数包装生成器来实现这一点(它立即调用.next()以便代码开始执行)。这些tasklet还可以与App Engine的rpc基础架构配合使用,从而可以使用任何现有的异步api调用。
使用ndb中使用的concurreny模型,您yield
可以是未来对象(类似于pep-3148中描述的对象)或App Engine rpc对象。当该rpc完成时,允许继续产生该对象的函数中的执行。
如果您使用的是从ndb.model.Model
派生的模型,那么以下内容将允许您异步迭代查询:
from ndb import tasklets
@tasklets.tasklet
def run():
it = iter(Entity.query())
# Other tasklets will be allowed to run if the next call has to wait for an rpc.
while (yield it.has_next_async()):
entity = it.next()
do_something_with(entity)
虽然ndb仍然被认为是实验性的(它的一些错误处理代码仍然需要一些工作),我建议你看看它。我在最近的两个项目中使用它,发现它是一个很棒的库。
请务必仔细阅读主页中链接的文档以及tasklet stuff的配套文档。