2015-03-13 58 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

    相关问题