【问题标题】:why the results of below two codes are different?为什么以下两个代码的结果不同?
【发布时间】:2017-11-08 02:16:10
【问题描述】:

代码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

【问题讨论】:

    标签: python yield finally


    【解决方案1】:

    在sn-p 1中,Python执行时

    print(af().next())
    

    成功了

          af().next()
    

    然后生成器对象的引用计数下降到0。此时,Python调用生成器的close方法强制finally块和__exit__方法运行,所以finally块打印end .

    然后print(af().next())print 部分运行并打印65


    在sn-p 2中,Python执行时

    print(g.next())
    

    生成器对象通过来自g 变量的引用保持活动状态,因此此时finally 块不会运行,Python 会打印65

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

    【讨论】:

      【解决方案2】:

      这取决于何时清理生成器本身。当您不保存对生成器的引用时,只要最后一个引用超出范围(至少在 CPython 上,“引用”解释器,这里是恰当的,因为它是引用计数的),生成器就会被清理,这会立即在生成器的主体中引发GeneratorExit,使其到达finally 块并打印。

      在您的第一个示例中,print(af().next())next 返回的那一刻,不再有对生成器的任何活动引用,因此它被清理,导致finally 块立即执行(print 没有尚未被调用,我们仍在获取要传递给它的参数)。

      在您的第二个示例中,您在 g 中保存了对生成器的引用,因此直到 g 被销毁(在本例中为程序退出时),生成器仍准备好恢复(它将在 @987654329 之后恢复@)。 print 完成,然后程序结束程序清理并执行 finally 块。

      请注意,不能保证像这样清除全局变量; CPython 2.7 碰巧在关机时将所有全局变量设置为None,这会让你产生这种行为,但这不是合同规定的;你想明确地用完或close生成器(如果需要,通过contextlib.closing)以确保它真的被清理了。

      【讨论】:

        猜你喜欢
        • 2021-10-14
        • 2018-04-15
        • 2021-04-01
        • 2021-08-31
        • 2015-09-15
        • 1970-01-01
        • 1970-01-01
        • 2019-07-08
        • 2020-01-25
        相关资源
        最近更新 更多