如果对象的__del__
未完成(例如通过抛出异常),是否会调用__init__
?
答案 0 :(得分:24)
class test():
def __init__(self):
raise
def __del__(self):
print "__del__ called"
try:
test()
except:
pass
是
说明: __del__
在删除对象的最后一个引用时被调用。但是如果你没有捕获异常,那么__del__
将不会被调用,因为python仍然保持对栈中对象的引用,并且它一直保持到程序退出以便打印回溯。如果捕获并处理异常,则只要从堆栈中丢弃与异常相关的所有信息,就会删除该对象。
当然,如果程序即将退出,__del__
无法保证成功运行,除非小心,在某些情况下甚至不行 - 请参阅__del__
warning。
Addenum:CédrikJulien在他的回答(现已修订)中说:“如果__new__
引发异常,则不会创建您的对象,__del__
将不会被称为“。这并不总是正确的。这是一个示例,即使__del__
中引发了异常,也会调用__new__
:
class test():
def __new__(cls):
obj = object.__new__(cls)
raise
return obj
def __del__(self):
print "__del__ called"
因此,在返回test
对象obj
之前,我们对__del__
对象执行了一些操作。但由于对象已经创建,因此调用__del__
。教训是:object.__new__()
方法中的,不要假设__del__
实际发生后应该发生的任何事情。否则,您可能会尝试访问不存在的属性或依赖其他一些无效的假设引发异常。该异常将被忽略,但任何{{1}}应该完成的任务也将失败。
答案 1 :(得分:14)
答案是是。
正如所讨论的那样here __del__
不是__init__
的反面,而只是__new__
的对立面。
如果__new__
在创建对象之前引发异常(通常在超类调用之前),则不会创建对象,也不会调用__del__
,否则创建对象__del__
将被调用。
__init__
方法是一个后期初始化器,真正的对象构造函数是__new__()
方法。
答案 2 :(得分:2)
编辑:如果您的问题将会__del__()
完全被调用,那么答案是:是。
如果,OTOH,你的问题会立即调用吗?,那么答案是:
不,不一定:
class Parent(object):
def __init__(self):
try:
Child(self)
except:
pass
class Child(object):
def __init__(self, parent):
parent.child = self
raise
def __del__(self):
print 'Child.__del__()'
p = Parent()
print 'hu?'
print p.child
print 'wha?'
输出:
hu?
<__main__.Child object at 0x7ff4674c>
wha?
Child.__del__()
当Child.__del__()
失败时, Child.__init__()
不在此处调用,但是当p
被垃圾收集时,即程序结束时。{/ p>
答案 3 :(得分:1)
我第二个 lazyrs '回答。但是,我想强调关于最后一个参考的观点。
如果在引发异常之前将self
传递给其他对象(例如,在注册回调时),则您的对象仍然存活,即不会调用__del__
:
>>> keeprefs = []
>>> class X(object):
... def __init__(self):
... keeprefs.append(self)
... raise Exception()
... def foobar(self):
... print("this could be a zombie foobar")
...
>>> x = X()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __init__
Exception
>>> keeprefs
[<__main__.X object at 0x7f9e2630bc50>]
>>> keeprefs[0].foobar()
this could be a zombie foobar
在现实生活中,keeprefs
可以是一个框架,您可以在其中注册某些事件的回调方法。因此,当您的异常被引发时,框架无法识别这一点并愉快地调用您显然从未创建过的对象的方法。
@ pillmuncher的答案也说明了最后一个引用问题,但我认为框架/回调示例更令人吃惊。