del调用了一个没有完成init的对象吗?

时间:2011-06-20 10:06:11

标签: python python-3.x

如果对象的__del__未完成(例如通过抛出异常),是否会调用__init__

4 个答案:

答案 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的答案也说明了最后一个引用问题,但我认为框架/回调示例更令人吃惊。