问题:什么是__del__方法,如何调用?
我正在阅读代码。有一个__del__
定义方法的类。我发现此方法用于销毁该类的实例。但是,我找不到使用此方法的地方。这样做的主要原因是我不知道如何使用此方法,可能不是这样:obj1.del()
。那么,我的问题是如何调用该__del__
方法?
回答 0
__del__
是终结器。当删除对象的所有引用之后的某个时刻发生垃圾回收时,调用该方法。
在一个简单的例子中,这可能是在您说完之后,del x
或者如果x
是局部变量,则在函数结束之后。特别是,除非有循环引用,否则CPython(标准Python实现)将立即进行垃圾回收。
但是,这是CPython 的实现细节。Python垃圾回收的唯一必需属性是,它会在删除所有引用之后发生,因此这可能在此之后没有必要发生,也可能根本没有发生。
此外,由于多种原因,变量可以生存很长一段时间,例如,传播异常或模块自省可以使变量引用计数保持大于0。此外,变量可以是引用循环的一部分-启用垃圾回收的CPython多数会中断,但不是全部,这样的周期,甚至只是周期性的。
由于您无法保证它会被执行,因此永远不要将您需要运行的代码放入其中__del__()
-而是,该代码属于块的finally
子句try
或with
语句中的上下文管理器。不过,也有有效的使用情况为__del__
:例如,如果一个对象X
的引用Y
,也保留副本Y
参考在全球cache
(cache['X -> Y'] = Y
),那么这将是一个有礼貌X.__del__
也删除缓存条目。
如果您知道析构函数提供了必要的清除操作(违反了上述准则),则您可能希望直接调用它,因为该方法没有什么特别之处:x.__del__()
。显然,仅当您知道不介意被两次调用时,才应该这样做。或者,作为最后的选择,您可以使用重新定义此方法
type(x).__del__ = my_safe_cleanup_method
回答 1
我写下了另一个问题的答案,尽管这是一个更准确的问题。
这是一个有点自以为是的答案。
不要使用__del__
。这不是C ++或为析构函数构建的语言。该__del__
方法确实应该在Python 3.x中消失,尽管我确信有人会发现一个有意义的用例。如果您需要使用__del__
,请注意每个http://docs.python.org/reference/datamodel.html的基本限制:
__del__
在垃圾回收器恰好收集对象时调用,而不是在丢失对对象的最后一个引用时而不是在执行时调用del object
。__del__
负责调用__del__
超类中的任何一个,尽管尚不清楚它是按方法解析顺序(MRO)还是仅调用每个超类。- 拥有一种
__del__
手段,垃圾收集器就放弃检测和清除任何循环链接,例如丢失对链接列表的最后一个引用。您可以获取gc.garbage中忽略的对象的列表。您有时可以使用弱引用来完全避免循环。有时会对此进行辩论:请参阅http://mail.python.org/pipermail/python-ideas/2009-October/006194.html。 - 该
__del__
函数可以作弊,保存对对象的引用,并停止垃圾回收。 - 显式引发的异常将
__del__
被忽略。 __del__
补充__new__
远远不止于__init__
。这变得令人困惑。见http://www.algorithm.co.il/blogs/programming/python-gotchas-1- 德尔 -is-不是最相反OF- 的init /一个解释和陷阱。__del__
在Python中不是一个“受欢迎的”孩子。您会注意到sys.exit()文档没有指定是否在退出之前收集垃圾,并且存在很多奇怪的问题。调用__del__
on全局变量会导致奇怪的排序问题,例如http://bugs.python.org/issue5099。__del__
即使__init__
失败也应该打电话吗?有关长线程的信息,请参见http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423。
但另一方面:
__del__
意味着您不会忘记调用结束语。有关专业意见,请参见http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python/__del__
。这通常与释放ctype或其他一些特殊资源有关。
我不喜欢该__del__
功能的原因。
- 每当有人提出来时,
__del__
它就会演变成三十种混乱的信息。 - 它在Python的Zen中打破了这些项目:
- 简单胜于复杂。
- 特殊情况不足以违反规则。
- 错误绝不能默默传递。
- 面对模棱两可的想法,拒绝猜测的诱惑。
- 应该有一种(最好只有一种)明显的方式来做到这一点。
- 如果实现难以解释,那是个坏主意。
因此,找到不使用的理由__del__
。
回答 2
该__del__
方法将在垃圾回收对象时被调用。请注意,不一定要调用它。以下代码本身不一定会这样做:
del obj
原因是del
只是将引用计数减一。如果还有其他对象引用,__del__
则不会调用。
__del__
尽管有一些注意事项。通常,它们通常不是很有用。在我看来,这更像是您要使用close方法或with语句。
请参阅有关__del__
方法的python文档。
需要注意的另一件事: __del__
如果使用过多的方法,可能会阻止垃圾回收。特别是,使用一种__del__
方法具有多个对象的循环引用将不会收集垃圾。这是因为垃圾收集器不知道首先调用哪个。有关更多信息,请参见gc模块上的文档。
回答 3
__del__
当对象最终被销毁时,将调用该方法(请注意拼写!)。从技术上讲(在cPython中),即不再有对您对象的引用,即对象超出范围。
如果要删除对象并因此调用__del__
方法,请使用
del obj1
它将删除该对象(假设没有其他引用)。
我建议你写一个这样的小班
class T:
def __del__(self):
print "deleted"
并在python解释器中进行调查,例如
>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
... a = T()
... print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>
请注意,关于删除和__del__
调用对象的确切时间,jython和ironpython具有不同的规则。__del__
由于这个原因以及调用对象和其环境可能处于未知状态这一事实,因此不认为使用该方法是一种好习惯。也不绝对保证__del__
会被调用-解释器可以以各种方式退出而不删除所有对象。
回答 4
如前所述,该__del__
功能有些不可靠。在看起来有用的情况下,请考虑使用__enter__
and __exit__
方法。这将产生类似于with open() as f: pass
用于访问文件的语法的行为。__enter__
进入时with
,__exit__
会自动调用,退出时会自动调用。有关更多详细信息,请参见此问题。