为什么以下两个代码的结果不同?

时间:2017-11-08 02:16:11

标签: python yield finally

代码1:

def af():
    a=65
    try:
       yield a

    finally:
        print('end')

print(af().next())

码2:

def af():
    a=65
    try:
       yield a

    finally:
        print('end')

g=af()
print(g.next())

code1的结果是:

end
65

但是code2的结果是:

65
end

2 个答案:

答案 0 :(得分:0)

在代码段1中,当Python执行时

print(af().next())

它通过

      af().next()

然后生成器对象的引用计数降为0.此时,Python调用生成器的close方法来强制finally块和__exit__方法运行,因此{{ 1}}块打印finally

然后 end的{​​{1}}部分会运行并打印print

在代码段2中,当Python执行时

print(af().next())

生成器对象通过65变量的引用保持活动状态,因此此时print(g.next()) 块不会运行,并且Python打印g

然后,在解释器关闭期间,生成器的引用计数降为0,finally触发65块,打印close。这不能保证会发生 - Python不保证在解释器关闭时活动的对象会调用它们的析构函数 - 但它确实发生了。

答案 1 :(得分:0)

关于发电机本身何时清理的问题。当你没有保存对生成器的引用时,一旦最后一个引用超出范围(至少在CPython上,"引用"解释器,这是适当的,因为它&#39 ; s引用计数),清理生成器,立即在生成器的主体中引发GeneratorExit,使其到达finally块并打印。

在您的第一个示例中print(af().next())返回next时,不再有任何生成引用,因此它已被清理,导致finally阻止要立即执行(print尚未调用,我们仍然会获得传递给它的参数。)

在第二个示例中,您在g中保存了对生成器的引用,因此在g被销毁之前(在这种情况下在程序退出时),生成器仍然可以恢复(它将恢复在yield之后。 print完成后,程序将结束程序清理并执行finally块。

请注意,像这样的全局变量的清理是不能保证的; CPython 2.7碰巧在关机时将所有全局变量设置为None,这会让你产生这种行为,但它不是契约性的;您希望明确地用完或close生成器(如果需要,可以通过contextlib.closing),以确保它真正得到清理。