Python 在生成器中使用with语句是否明智?
考虑以下Python代码: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中确实不起作用,所以应该将其视
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()
迭代器被垃圾收集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回答得很好。我只是想用一种更好的方式来处理它。