如果Python生成器不再被使用但尚未达到StopIteration,那么它是否会被垃圾收集?

时间:2013-03-19 01:45:51

标签: python garbage-collection generator coroutine

当不再使用发电机时,应该进行垃圾收集,对吧?我尝试了以下代码,但我不确定哪个部分我错了。

import weakref
import gc

def countdown(n):
    while n:
        yield n
        n-=1

cd = countdown(10)
cdw = weakref.ref(cd)()
print cd.next()
gc.collect()
print cd.next()
gc.collect()
print cdw.next()

在倒数第二行,我调用了垃圾收集器,因为不再调用cdgc应免费cd。但是当我打电话给cdw.next()时,它仍然打印8.我再试了几次cdw.next(),它可以成功打印所有其余的直到StopIteration。

我试过这个,因为我想了解生成器和协程如何工作。在David Beazley的PyCon演讲“关于协同程序和并发的好奇课程”的幻灯片28中,他说一个协程可以无限期地运行,我们应该使用.close()来关闭它。然后他说垃圾收集器会拨打.close()。根据我的理解,一旦我们自己致电.close()gc将再次致电.close()gc会收到一条警告,指出它无法在已经关闭的协程上调用.close()吗?

感谢您的任何意见。

4 个答案:

答案 0 :(得分:10)

由于python的动态特性,在到达当前例程的末尾之前不会释放对cd的引用,因为(至少)python的Cpython实现没有“预读”。 (如果你不知道你正在使用什么python实现,那几乎肯定是“Cpython”)。如果一个对象在一般情况下仍然存在于当前命名空间中,那么解释器几乎不可能确定某个对象是否应该是空闲的(例如,您仍然可以通过调用{{1}来访问它})。

在一些不太常见的情况下,其他python实现可能能够在当前堆栈帧结束之前释放对象,但Cpython不会打扰。

尝试使用此代码,该代码演示了可以在Cpython中自由清理生成器:

locals()

当引用计数达到0时,对象(包括生成器)被垃圾收集(在Cpython中 - 其他实现可能以不同的方式工作)。在Cpython中,引用计数仅在您看到import weakref def countdown(n): while n: yield n n-=1 def func(): a = countdown(10) b = weakref.ref(a) print next(a) print next(a) return b c = func() print c() 语句时或者由于当前名称空间更改而导致对象超出范围时递减。

重要的是,一旦没有对象的引用,垃圾收集器就可以自由清理它。有关如何确定没有更多引用的详细信息留给您正在使用的特定python发行版的实现者。

答案 1 :(得分:8)

在您的示例中,生成器在脚本结束之前不会收集垃圾。 Python不知道你是否会再次使用cd,所以它不能扔掉它。准确地说,全局命名空间中的生成器仍然存在引用

引用计数降至零时,生成器将获得GCed,就像任何其他对象一样。即使发电机没用完。

这可能发生在许多正常情况下 - 如果它的本地名称超出范围,如果它是del,如果它的所有者获得GCed。但是,如果任何活动对象(包括名称空间)拥有对它的强引用,它将不会获得GCed。

答案 2 :(得分:4)

Python垃圾收集器并不那么聪明。即使您在该行之后不再引用cd,引用仍然存在于局部变量中,因此无法收集。 (事实上​​,你正在使用的一些代码可能会在你的局部变量中挖掘它并复活它。不太可能,但可能。所以Python不能做出任何假设。)

如果您想让垃圾收集器在这里做一些事情,请尝试添加:

del cd

这将删除局部变量,允许收集对象。

答案 3 :(得分:0)

其他答案解释说gc.collect()不会垃圾收集任何仍然引用它的东西。生成器仍有实时参考cd,因此在删除cd之前不会进行gc编辑。

然而,此外,OP正在使用此行创建一个对该对象的SECOND强引用,该行调用弱引用对象:

cdw = weakref.ref(cd)()

因此,如果要做del cd并致电gc.collect(),则生成器不会被gc'因为cdw也是参考。

要获取实际的弱引用,请不要调用weakref.ref对象。只需这样做:

cdw = weakref.ref(cd)

现在删除cd并收集垃圾时,引用计数将为零,并且调用弱引用将导致None,如预期的那样。