Python 在生成器中使用with语句是否明智?

Python 在生成器中使用with语句是否明智?,python,generator,with-statement,contextmanager,Python,Generator,With Statement,Contextmanager,考虑以下Python代码: def values(): with somecontext(): yield 1 yield 2 for v in values(): print(v) break 在这种情况下,Python是否保证生成器正确关闭,从而退出上下文 我意识到,在实践中,由于引用计数和对生成器的急切销毁,CPython中也会出现这种情况,但是Python能保证这种行为吗?我确实注意到它在Jython中确实不起作用,所以应该将其视

考虑以下Python代码:

def values():
    with somecontext():
        yield 1
        yield 2
for v in values():
    print(v)
    break
在这种情况下,Python是否保证生成器正确关闭,从而退出上下文


我意识到,在实践中,由于引用计数和对生成器的急切销毁,CPython中也会出现这种情况,但是Python能保证这种行为吗?我确实注意到它在Jython中确实不起作用,所以应该将其视为bug或允许的行为吗?

是的,您可以在生成器中使用
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
这是的一部分,关闭生成器会引发异常。收割没有引用的发电机应该总是关闭发电机,如果Jython没有关闭发电机,我会认为这是一个bug。 见规范摘要第4点和第5点:

  • 为生成器迭代器添加一个
    close()
    方法,该方法将在生成器暂停时引发
    GeneratorExit
    。如果 然后,生成器将引发
    StopIteration
    (通过正常退出,或 由于已关闭)或
    GeneratorExit
    (未捕获 异常),
    close()
    返回其调用者。如果发电机 如果生成一个值,则引发
    运行时错误
    。如果发电机 引发任何其他异常,并将其传播到调用方。
    close()
    异常或正常退出

  • 添加支持以确保在生成器运行时调用
    close()
    迭代器被垃圾收集

  • 唯一需要注意的是,在Jython、IronPython和PyPy中,不能保证在退出解释器之前运行垃圾收集器。如果这对应用程序很重要,可以显式关闭生成器:

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

    或者显式触发垃圾收集。

    如果您强调的是安全性,则始终可以将生成器包装在
    上下文库中。关闭
    -这似乎是最简单的解决方案:

    from contextlib import closing
    
    with closing(gen_values()) as values:
        for value in values:
            ...
    
    事实上,如果是我,我会把函数写成

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

    确保任何用户必须将其放入
    中才能使用它。

    我在langdoc中显然找不到任何保证这种行为的语句。正如我在问题中所写的,我意识到这将是一种常见的做法,但依赖它安全吗?我应该向Jython的人报告这个功能的缺失吗?@Dolda2000:我正在构建一个测试用例来展示;这听起来像Jython在这方面有一个bug。@马蒂耶恩皮特斯:的确如此,但是在下一个GC发生之前留下一个潜在的敏感上下文并不是我通常认为的“安全”。例如,如果留下一个互斥锁,那就太糟糕了。@MartijnPieters:那么,也许我误读了这里的故事情节。我想我们想知道Python语言是否保证我们会看到退出,而您收集的证据表明“我们承诺在垃圾收集中看到它,这可能永远不会发生”。当然,但考虑到所有的警告,我认为这个问题的答案应该是“否”,而不是“是”。)嗯,对,;谢谢你的回答,但我已经意识到,如果我想确定的话,我可以使用显式上下文。问题主要是我是否真的需要它@Dolda2000当然,但我觉得Martijn回答得很好。我只是想用一种更好的方式来处理它。