在生成器中使用with语句明智吗?

6

考虑以下Python代码:

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

在这种情况下,Python是否保证生成器被正确关闭,因此上下文被退出?
我意识到,在实践中,由于引用计数和生成器的急切销毁,CPython将会是这种情况,但是Python是否保证这种行为呢?我确实注意到在Jython中它确实不起作用,那么这应该被认为是一个错误还是允许的行为?
2个回答

4

是的,你可以在生成器中使用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没有关闭生成器,则认为是一个错误。
请参见规范摘要中的4和5点。
  1. 为生成器迭代器添加一个close()方法,在生成器暂停的位置引发GeneratorExit异常。如果生成器随后引发StopIteration(通过正常退出或因已关闭)或GeneratorExit(未捕获异常),close()返回到其调用者。如果生成器产生值,则会引发RuntimeError异常。如果生成器引发任何其他异常,则将传播给调用者。close()如果生成器已由于异常或正常退出而退出,则不执行任何操作。

  2. 添加支持以确保在垃圾回收生成器迭代器时调用close()

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

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

或者显式地触发垃圾回收。

@Dolda2000:我正在为您构建一个测试用例来展示;听起来Jython在这方面存在一个bug。 - Martijn Pieters
@Dolda2000:为您添加了一个PEP参考。 - Martijn Pieters
1
@MartijnPieters:确实如此,但如果保持潜在的敏感环境处于活动状态直到下一次GC发生,这并不是我通常认为的“安全”的做法。:)例如,像那样保留互斥锁定会非常糟糕。 - Dolda2000
1
@MartijnPieters:也许我在这里误读了情节。 我认为我们想知道Python语言是否保证我们会看到Exiting,而你提供的证据表明“我们承诺在垃圾回收时看到它,但这可能永远不会发生”。 - DSM
2
当然可以,但是考虑到所有的警告,我认为对于这个问题的答案应该是“不”,而不是“是”。 :) - Dolda2000
显示剩余9条评论

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中才能使用它。


嗯,是的;谢谢你的回答,但我已经知道如果我想确保的话可以使用显式上下文。问题主要是关于我是否真的需要那个。 :) - Dolda2000
@Dolda2000 当然,但我觉得Martijn已经回答得很好了。我只是提供了一种更好的处理方式。 - Veedrac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接