我正在阅读代码。在其中定义了__del__方法的类。我发现此方法用于销毁该类的实例。但是,我找不到使用此方法的地方。这样做的主要原因是我不知道如何使用此方法,可能不是这样:obj1.del()。所以,我的问题是如何调用__del__方法?

#1 楼

__del__是终结器。当删除对象的所有引用之后的某个时刻发生垃圾回收时,调用该方法。

在一个简单的例子中,这可能是在您说del x之后,或者如果x是局部变量,则在函数结束之后。特别是,除非有循环引用,否则CPython(标准Python实现)将立即进行垃圾回收。

但是,这是CPython的实现细节。 Python垃圾回收的唯一必需属性是,它会在删除所有引用之后发生,因此这可能在此之后没有必要立即发生,也可能根本没有发生。

此外,由于多种原因,变量可以长期存在。传播性异常或模块自省可以使变量引用计数大于0。而且,变量可以是引用循环的一部分—启用垃圾回收的CPython大部分(但不是全部)中断此类循环,甚至是周期性地中断。 br />
由于不能保证它已执行,因此永远不要将需要运行的代码放入__del__()中-相反,该代码属于finally块的try子句或with中的上下文管理器声明。但是,__del__有有效的使用案例:例如如果对象X引用了Y,并且还在全局Ycache)中保留了cache['X -> Y'] = Y引用的副本,则X.__del__也删除缓存条目是有礼貌的。

如果您知道析构函数提供了(违反上述准则)必需的清理,您可能希望直接调用它,因为它没有特别的方法:x.__del__()。显然,仅当您知道不介意被两次调用时,才应该这样做。或者,作为最后的选择,您可以使用

type(x).__del__ = my_safe_cleanup_method  

重新定义此方法。

评论


您说CPython在其引用计数减少为零后立即删除对象的功能是“实现细节”。我不相信。您能否提供备份该声明的链接? (我的意思是,加粗字体本身很令人信服,但链接紧随其后... :-)

– Stuart Berg
2012年12月3日19:13

CPython实现细节:CPython当前使用引用计数方案,并(可选)延迟检测循环链接的垃圾,...其他实现的行为有所不同,CPython可能会发生变化。 (docs.python.org/2/reference/datamodel.html)

– ilya n。
13年11月27日在16:13

在这种情况下,__ exit__怎么了?它是在__del__之前还是之后运行,还是一起运行?

–lony
16年5月11日在16:05

程序终止时是否包括“可能根本不会发生”?

–安迪·海登(Andy Hayden)
17年11月6日在20:47

@AndyHayden:即使在程序终止时__del__方法也可能无法运行,即使它们确实在终止时运行,编写__del__方法也能正常工作,即使解释器忙于自毁时也需要比许多程序员更仔细地编码。 (CPython清理通常使__del__方法在解释器关闭时运行,但是在某些情况下这还不够。守护进程线程,C级全局变量以及在另一个__del__中创建的__del__的对象都可能导致__del__方法无法运行。 )

– user2357112支持Monica
18-2-15在21:40



#2 楼

我写下了另一个问题的答案,尽管这是一个更准确的问题。

构造函数和析构函数如何工作?

这里是一个有点自以为是的答案。

不要使用__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-del-is-not-the-opposite-of-init/。

__del__在Python中不是一个“受欢迎的”孩子。您会注意到sys.exit()文档没有指定是否在退出之前收集垃圾,并且存在很多奇怪的问题。在全局变量上调用__del__会导致出现奇怪的排序问题,例如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__的原因。

评论


即使问题不完全是:为什么我们不应该使用__del__而是如何调用__del__,您的回答却很有趣。

–nbro
2015年1月5日20:00

在其他新闻中,我忘了提及PyPy(运行时间更长的应用程序的更快的解释器)将在del上中断。

–查尔斯·梅里亚姆
17年1月26日,下午3:12

感谢@Gloin更新断开的链接!

–查尔斯·梅里亚姆
17年4月17日在16:18

@CharlesMerriam谢谢您的回答!

– Tom Burrows
17年4月17日在17:43

您已经提到“在垃圾回收器碰巧正在收集对象时,而不是在丢失对对象的最后引用时,就会调用del”。但是,文档明确指出:“注意:del x不会直接调用x .__ del __()-前者将x的引用计数减一,而后者仅在x的引用计数达到零时才调用”。因此,当您丢失对该对象的最后一个引用时,总是会调用del。

–图萨·瓦兹拉尼(Tushar Vazirani)
17年11月12日在20:10



#3 楼

__del__方法,当对象被垃圾回收时将被调用。请注意,虽然不一定保证可以调用它。以下代码本身不一定会执行此操作:

del obj


原因是del只是将引用计数减一。如果还有其他对象引用,则不会调用__del__

使用__del__有一些警告。通常,它们通常不是很有用。对我来说,听起来更像是您要使用close方法或with语句。

请参阅有关__del__方法的python文档。

要注意的另一件事:__del__如果使用过多,方法可能会阻止垃圾收集。尤其是,使用__del__方法具有多个对象的循环引用将不会收集垃圾。这是因为垃圾收集器不知道首先调用哪个。有关更多信息,请参见gc模块上的文档。

#4 楼

当对象最终被销毁时,将调用__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
>>>   


请注意,jython和ironpython具有不同的规则关于何时删除对象并调用__del__的确切信息。尽管由于这个原因以及调用该对象及其环境可能处于未知状态这一事实,也不认为使用__del__是一种好习惯。也不能绝对保证会调用__del__-解释器可以以各种方式退出而不删除所有对象。

评论


与stackoverflow.com/a/2452895/611007和stackoverflow.com/a/1481512/611007相比,使用del obj1似乎是个坏主意。

–n611x007
2013年9月10日于13:42

#5 楼

如前所述,__del__的功能有些不可靠。如果它似乎有用,请考虑改用__enter____exit__方法。这将产生类似于用于访问文件的with open() as f: pass语法的行为。进入__enter__的范围时,将自动调用with,而退出时将自动调用__exit__。有关更多详细信息,请参见此问题。