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

Dol*_*000 5 python generator with-statement contextmanager

考虑以下 Python 代码:

def values():
    with somecontext():
        yield 1
        yield 2
for v in values():
    print(v)
    break
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Python 是否保证生成器已正确关闭,从而退出上下文?

我意识到在实践中,由于引用计数和生成器的急切破坏,在 CPython 中会出现这种情况,但是 Python 是否保证这种行为?我确实注意到它在 Jython 中确实不起作用,所以这应该被视为错误还是允许的行为?

Mar*_*ers 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
Run Code Online (Sandbox Code Playgroud)

这是PEP 342的一部分,其中关闭生成器会引发异常。收获没有留下引用的生成器应该始终关闭该生成器,如果 Jython 没有关闭生成器,我会认为这是一个错误。

请参阅规范摘要的第 4 点和第 5 点:

  1. close()为生成器迭代器添加一个方法,该方法GeneratorExit在生成器暂停的地方引发。如果生成器随后引发StopIteration(通过正常退出,或由于已经关闭)或GeneratorExit(通过不捕获异常),close()则返回到其调用者。如果生成器产生一个值,则 aRuntimeError被提升。如果生成器引发任何其他异常,则会将其传播给调用者。 close()如果生成器由于异常或正常退出而已经退出,则不执行任何操作。

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

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

gen = values()
next(gen)
gen.close()
Run Code Online (Sandbox Code Playgroud)

或显式触发垃圾收集。

  • 当然,但考虑到所有注意事项,我认为问题的答案应该是“否”而不是“是”。:) (2认同)