Reputation: 118540
Will __del__
be called if an object's __init__
does not complete (such as by throwing an exception)?
Upvotes: 21
Views: 3177
Reputation: 50995
class test():
def __init__(self):
raise
def __del__(self):
print "__del__ called"
try:
test()
except:
pass
Yes.
Explanation: __del__
is called when the last reference to the object is removed. But if you do not catch the exception, __del__
will not be called because the python still keeps a reference to the object in the stack, and it's kept until the program exits in order to print the traceback. If you catch and handle the exception, the object is deleted as soon as the all the information relating to the exception is discarded from the stack.
Of course, __del__
is not guaranteed to run successfully if the program is about to quit, unless care is taken, and in some circumstances not even then -- see __del__
warning.
Addenum: Cédrik Julien said in his answer (now amended): "If __new__
raised an exception, your object won't be created and __del__
won't be called". This is not always right. Here's an example where __del__
is called even though the exception is raised in __new__
:
class test():
def __new__(cls):
obj = object.__new__(cls)
raise
return obj
def __del__(self):
print "__del__ called"
So some exception occurred while we did some stuff to the test
object obj
before returning it. But since the object was already created, __del__
is called. The lesson is: in the __del__
method, don't assume anything that was supposed to happen after object.__new__()
has in fact happened. Otherwise you could raise an exception trying to access a non-existing attribute or by relying on some other assumption that is not valid. The exception will be ignored, but whatever task __del__
was supposed to accomplish will fail as well.
Upvotes: 27
Reputation: 9953
I second lazyrs' answer. However, I'd like to stress the point about the last reference.
If you pass self
around to other objects (e.g. when registering callbacks) before the exception is raised, your object will still be alive, i.e. __del__
is not called:
>>> 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
In real life keeprefs
could be, for instance, a framework where you register callback methods for some events. So when your exception is raised, the framework does not recognize this and happily calls methods of your apparently never created object.
@pillmuncher's answers also illustrates the last reference problem, but I think the framework/callback example is more startling.
Upvotes: 1
Reputation: 10162
EDIT: If your question is Will __del__()
be called at all?, then the answer is: Yes.
If, OTOH, your qestion is Will it be called immediately?, then the answer is:
No, not necessarily:
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?'
Output:
hu?
<__main__.Child object at 0x7ff4674c>
wha?
Child.__del__()
Child.__del__()
isn't called here when Child.__init__()
fails, but when p
is garbage collected, which is when the program ends.
Upvotes: 2
Reputation: 80761
Answer is YES.
As discussed here __del__
is not the opposite of __init__
, but merely the oppposite of __new__
.
If __new__
raise an exception before the object creation (in general before the superclass call), your object won't be created and __del__
won't be called, otherwise, object is created and __del__
will be called.
The __init__
method is a late initializer, the real object constructor is the __new__()
method.
Upvotes: 14