2011-09-01 77 views
1

我正在實現一個實用程序庫,它是一種旨在在Google App Engine雲計算服務的分佈式環境中運行的任務管理器。 (它使用任務隊列和內存緩存的組合來執行後臺處理)。我打算使用生成器來控制任務的執行,基本上通過在用戶代碼中使用yield來強制執行非搶先式「併發」。Python生成器在單獨函數中的「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

'yield'使函數成爲一個生成器 - 如果它不在'self.run'中,'run'不是一個生成器,如果它在'self.sleep'中,'sleep'是一個生成器。這就是你如何在Python中定義一個生成器。所以你的問題變成了「我如何製造一臺不是發電機的發電機」或者「我怎麼不把發電機製成發電機」 - 這些都不合理。 – agf

+0

另一個解決方案是製作自己的可迭代對象,而不是使用生成器。儘管如此,這可能會有更多的工作,並且可能沒有理由。 –

回答

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) 

看起來不錯我。

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

我建議你看看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的配套文檔。