2015-03-13 57 views
4

考慮以下Python代碼:與發電機中的語句一起使用是否明智?

def values(): 
    with somecontext(): 
     yield 1 
     yield 2 
for v in values(): 
    print(v) 
    break 

在這種情況下,做的Python保證發電機正常關閉,因此,該情況下退出?

我意識到,實際上,由於引用計數和對生成器的急切破壞,CPython將會是這種情況,但是Python是否能保證這種行爲?我注意到它在Jython中確實不起作用,那麼它應該被認爲是一個錯誤或允許的行爲?

回答

3

是的,您可以在發生器中使用with語句而不會出現問題。 Python將正確處理上下文,因爲當收集垃圾時,生成器將被關閉。

在發電機發電機垃圾回收時,一個GeneratorExit引發異常,因爲它會在那個時候被關閉:

>>> from contextlib import contextmanager 
>>> @contextmanager 
... def somecontext(): 
...  print 'Entering' 
...  try: 
...   yield None 
...  finally: 
...   print 'Exiting' 
... 
>>> def values(): 
...  with somecontext(): 
...   yield 1 
...   yield 2 
... 
>>> next(values()) 
Entering 
Exiting 
1 

這是PEP 342,在關閉發電機引發異常的一部分。如果Jython沒有關閉生成器,收穫沒有引用的生成器應該總是關閉該生成器。我認爲這是一個bug。

參見規格概要的點4和5:

  • 添加發電機迭代器close()方法,其中在生成器被暫停的點引發GeneratorExit 。如果 發電機然後提高StopIteration(通過正常退出,或 由於已關閉)或GeneratorExit(通過不捕獲 的例外),close()返回給它的調用者。如果發電機 產生一個值,則產生一個RuntimeError。如果發生器 引發任何其他異常,它將傳播給調用者。 close()如果由於 異常或正常退出而發生器已經退出,則不執行任何操作。

  • 添加支持以確保在發生器 垃圾回收時調用close()。然後

  • 唯一要注意的是,在Jython中,IronPython和PyPy垃圾收集器不能保證離開解釋之前運行。如果這對您的應用程序非常重要,則可以明確關閉生成器:

    gen = values() 
    next(gen) 
    gen.close() 
    

    或明確觸發垃圾回收。

    +0

    我無法明確地在langdoc中找到保證這種行爲的任何語句。正如我在這個問題中寫的那樣,我意識到在普通實踐中會出現這種情況,但依靠它是否安全?我應該向Jython人員報告缺乏這種功能嗎? – Dolda2000 2015-03-13 19:35:02

    +0

    @ Dolda2000:我正在給你一個測試用例來展示;聽起來像Jython在這方面有一個錯誤。 – 2015-03-13 19:35:36

    +0

    我已經有了一個測試用例,表明它在CPython上工作,但是我真正要問的是它應該被認爲是定義良好還是僅僅是CPython實現的意外。我在問,因爲我正在建立一個系統,我希望真正依靠這種行爲,而且它的正確性非常重要。 – Dolda2000 2015-03-13 19:37:09

    0

    如果你的重點是安全的,可以隨時包裹在contextlib.closing發電機 - 這似乎是最簡單的解決方案:

    from contextlib import closing 
    
    with closing(gen_values()) as values: 
        for value in values: 
         ... 
    

    事實上,如果是我我會寫的功能

    def gen_values(): 
        def inner(): 
         ... 
    
        return closing(inner()) 
    

    確保任何用戶必須將其放入with才能使用它。

    +0

    嗯,是的;感謝您的回答,但我已經意識到,如果我想確定,我可以使用明確的上下文。問題主要是關於我是否真的需要這個。 :) – Dolda2000 2015-03-13 22:57:31

    +0

    @ Dolda2000當然,但我覺得Martijn回答得很好。我只是給出一個更好的方式來處理它。 – Veedrac 2015-03-13 23:00:44

    相關問題