gpt4 book ai didi

python生成器垃圾收集

转载 作者:IT老高 更新时间:2023-10-28 22:15:17 25 4
gpt4 key购买 nike

我认为我的问题与 this 有关,但不完全相似。考虑这段代码:

def countdown(n):
try:
while n > 0:
yield n
n -= 1
finally:
print('In the finally block')

def main():
for n in countdown(10):
if n == 5:
break
print('Counting... ', n)
print('Finished counting')

main()

这段代码的输出是:

Counting...  10      
Counting... 9
Counting... 8
Counting... 7
Counting... 6
In the finally block
Finished counting

是否保证在“完成计数”之前打印“在 finally block 中”行?还是因为 cPython 的实现细节,当引用计数达到 0 时,对象将被垃圾回收。

我也很好奇 countdown 生成器的 finally block 是如何执行的?例如如果我将 main 的代码更改为

def main():
c = countdown(10)
for n in c:
if n == 5:
break
print('Counting... ', n)
print('Finished counting')

然后我确实看到 Finished count 打印在 in the finally block 之前。垃圾收集器如何直接进入 finally block ?我想我一直认为 try/except/finally 是从表面上看的,但是在生成器的上下文中思考让我三思而后行。

最佳答案

正如您所料,您依赖于 CPython 引用计数的特定于实现的行为。1

事实上,如果你在 PyPy 中运行这段代码,输出通常是:

Counting...  10
Counting... 9
Counting... 8
Counting... 7
Counting... 6
Finished counting
In the finally block

如果你在交互式 PyPy session 中运行它,最后一行可能会在很多行之后出现,甚至只有在你最终退出时才会出现。


如果你看看生成器是如何实现的,它们的方法大致是这样的:

def __del__(self):
self.close()
def close(self):
try:
self.raise(GeneratorExit)
except GeneratorExit:
pass

当引用计数变为零时,CPython 会立即删除对象(它还有一个垃圾收集器来分解循环引用,但这与这里无关)。一旦生成器超出范围,它就会被删除,所以它会被关闭,所以它会在生成器框架中引发一个 GeneratorExit 并恢复它。当然,GeneratorExit 没有处理程序,所以 finally 子句被执行,控制权向上传递到堆栈,异常被吞没。

在使用混合垃圾收集器的 PyPy 中,生成器直到 GC 决定扫描时才会被删除。在内存压力较低的交互式 session 中,这可能会延迟到退出时间。但一旦发生,同样的事情也会发生。

您可以通过显式处理 GeneratorExit 看到这一点:

def countdown(n):
try:
while n > 0:
yield n
n -= 1
except GeneratorExit:
print('Exit!')
raise
finally:
print('In the finally block')

(如果您关闭 raise,您将获得相同的结果,只是原因略有不同。)


您可以显式关闭一个生成器——而且,与上面的内容不同,这是生成器类型的公共(public)接口(interface)的一部分:

def main():
c = countdown(10)
for n in c:
if n == 5:
break
print('Counting... ', n)
c.close()
print('Finished counting')

或者,当然,您可以使用 with 语句:

def main():
with contextlib.closing(countdown(10)) as c:
for n in c:
if n == 5:
break
print('Counting... ', n)
print('Finished counting')

<子>1。如Tim Peters' answer指出,您在第二个测试中依赖于 CPython 编译器的特定于实现的行为。

关于python生成器垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50091553/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com