3

我试图了解 Python 垃圾收集器是如何工作的,以及我是否可以做任何事情来控制何时收集对象。我写了这个测试:

>>> class Test:
...     def __del__(self):
...         print 'Delete ' + str(self)
...
>>> def fun():
...    return Test()
...
>>> fun()
<__main__.Test instance at 0x0000000002989E48>
>>> fun()
Delete <__main__.Test instance at 0x0000000002989E48>
<__main__.Test instance at 0x00000000023E2488>
>>> fun()
Delete <__main__.Test instance at 0x00000000023E2488>
<__main__.Test instance at 0x0000000002989C48>

正如你所看到的Test,虽然我没有为它保留一个实例,但直到我下次调用时才会删除该实例fun。这仅仅是一个意外(它可能已在其他任何时候被删除)还是有一个特定的原因为什么它只有在我fun再次调用时才被删除?如果我不保留对它的引用,我能做些什么来确保它被删除?

4

2 回答 2

3

Python 垃圾收集器(与所有垃圾收集器一样)的“联系”是它会在对它的最后一个可访问引用消失后的某个时间释放一个对象。

因为 CPython 使用引用计数,作为一个实现细节,它将在对它们的最后一个可访问引用消失后立即释放大多数垃圾对象(特别是非循环对象) 。这不是 Python 语言的保证,对于 PyPy、Jython、IronPython 等其他 Python 实现也不适用,因此依赖它通常被认为是不好的做法。

在您的情况下,您在再次调用函数后所观察到的对象与垃圾收集器的行为几乎没有关系,而是由于交互式解释器外壳的工作方式。

当您在交互式提示中计算表达式时,结果值会自动保存在变量_中,因此如果您发现仅看到它打印后仍需要它,您可以将其取回。因此,在您fun()调用之后,仍然有对返回值的引用。然后,当您评估另一个表达式(其他任何内容,它不必fun再次涉及)时,_将被新值覆盖,从而使旧值被垃圾收集。

适用于在交互式提示中直接输入的表达式,因此不会延迟函数内对象的收集或 Python 代码作为脚本导入或运行时。

于 2013-07-14T22:45:01.613 回答
2

尝试显式调用del返回值:

returned_value = fun()
del returned_value

但是像这样的终结__del__器可能会出现问题;正如您已经看到的,一个问题是它们何时被调用不是确定性的。此外,可以在终结器中重新实例化已删除的对象,例如将对其的引用粘贴到全局列表中。

如果您需要释放资源(除了原始内存)——例如解锁锁、关闭文件或释放数据库连接,请使用上下文管理器,并使用with语句限制其生命周期。其中许多资源已经是上下文管理器。例如,可以使用以下方式隐式锁定和解锁 threading.Lock with

# "with" statement will call the __enter__ method of self.lock,
# which will block until self.lock can be locked
with self.lock:
    # do thread-synchronized stuff here

# self.lock is automatically released here - at then end of
# the "with" block, the lock's __exit__ method is called, which
# releases the lock. This will get called even if the block is 
# exited by a raised exception
于 2013-07-14T14:31:31.437 回答