Skip to main content
 首页 » 编程设计

Python 生成器的 'yield' 在单独的函数中

2025年05月04日15webabcd

我正在实现一个实用程序库,它是一种旨在在 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)