2012-12-03 21 views
3

__del__的Python文档解释了当对象的引用计数减为零时,可以调用对象的__del__方法。它是否总是立即被调用(即同步)?或者有可能将这种方法称为“将来的某个时刻”?python是否总是同时调用__del__?

此外,这个问题的答案是否适用于Python?或者是授予语言实现的一些纬度? (我用CPython的。)

对于这个问题,以下假设:

  • 有问题的对象从object继承。
  • 有问题的对象有一个自定义的__del__实现。
  • 对象的自定义__del__实现不会创建任何对象的新引用。
  • 有问题的对象不是参考周期的一部分。
  • 解释器并未处于关闭状态。

从我所知道的来看,围绕这个话题似乎有很多困惑。在努力侧步一些不必要的讨论,让我给不可接受的答案的一些例子:“!不要用__del__这是令人困惑的”

  • ”不要使用__del__!它不是'pythonic'。“
  • “请勿使用__del__,请改为使用上下文管理器。”
  • 如果没有引用可信来源,任何可以断定什么是或不能保证的答案。 (请随意引用其他SO答案,但不要将它们作为您的唯一来源。在这种情况下,“可信”意味着来自文档或信誉良好的人,或者甚至是CPython源本身。)

要改写这个问题用一个例子,考虑下面的代码:

class C(object): 
    def __init__(self, i): 
     self.i = i 

    def __del__(self): 
     print "C.__del__:", self.i 

print "Creating list" 
l = [C(0), C(1), C(2)] 

print "Popping item 1" 
l.pop(1) 

print "Clearing list" 
l = [] 

print "Exiting..." 

产生以下输出:

$ python test_del.py 
Creating list 
Popping item 1 
C.__del__: 1 
Clearing list 
C.__del__: 2 
C.__del__: 0 
Exiting... 

请注意,在这个例子中,__del__同步呼叫。这种行为是否由语言保证? (请记住上面列出的假设。)

+0

你的用例答案很重要? –

+0

如果我可以依靠这种行为,它允许一个更清洁的API设计。我没有强迫我的图书馆的用户记得在我给它们的对象上调用一些特殊的“cleanUp()”函数,我只需将必要的清理代码放在__del__中即可。例如,这是C++中良好的基于​​RAII的API设计的基础。 –

+1

但是,当* __del__被调用时,知道如何改变? (在任何情况下,该对象都将被删除*) –

回答

3

从Python语言参考(Python的2.7.3版),Chapter 3, Data model

的对象从未明确销毁;然而,当他们无法到达时,他们可能会被垃圾收集。允许实现推迟垃圾收集或完全忽略垃圾收集 - 只要没有收集到仍然可以访问的对象,实施质量如何实施垃圾收集就成了一个问题。

CPython的实现细节: CPython的目前使用的参考计数方案与(可选)延迟环状连接的垃圾,其中,一旦收集大多数对象,因为它们变得不可达的检测,但不能保证,以收集垃圾含循环参考。有关控制循环垃圾收集的信息,请参见gc模块的文档。其他实现的行为不同,CPython可能会改变。不要依赖对象在无法访问时立即敲定(例如:始终关闭文件)。

+0

感叹。我怎么错过了?我花了我的时间阅读'__del__'本身的文档,这并没有说明什么是实现定义。它只说'__del__'被称为“当x的引用计数达到零时调用”。但是你引用的段落明确表示为白天。 –

+0

接受这个答案,但也请参阅@安德鲁的答案(下面),这个链接与这个关于PyPy的讨论非常相关。 –

1

该行为不受语言保证。例如,请参见PyPy's documentation on their garbage collection scheme,其中明确指出在PyPy中调用__del__的时间与CPython不同。

如果你的对象不是参考周期的一部分,那么我明白这种行为在CPython中是可靠的,但我不知道有任何明确的保证。

+0

伟大的链接,谢谢。它给了我三个印象:(1)语言规范是模糊的,但它似乎意味着立即删除行为是预期的。 (2)这是一个任意的决定,可能受到CPython实施的影响。 (3)由于PyPy,Jython和IronPython的作者不会采用CPython的引用计数方案,所以它实际上*无关紧要。“官方”答案是什么。这个事实意味着'__del__'行为事实上是“未定义的”,不管标准作者是否关心。 –

+0

我不能说语言规范在这一点上是否不清楚,但从实际的角度来看,我同意这种行为是未定义的,不应该使用,除非您确实希望代码在对象从内存中删除(这似乎是一个不太可能的用例)。 –

+0

另外,注意:即使CPython是唯一的目标,我也不觉得应该编写假定对象不是参考周期一部分的代码。由于对象本身的外部情况,对象可以成为参考周期的一部分。 –

相关问题