Matt Joiner
Matt Joiner

Reputation: 118540

Is del called on an object that doesn't complete init?

Will __del__ be called if an object's __init__ does not complete (such as by throwing an exception)?

Upvotes: 21

Views: 3177

Answers (4)

Lauritz V. Thaulow
Lauritz V. Thaulow

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

Oben Sonne
Oben Sonne

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

pillmuncher
pillmuncher

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

C&#233;dric Julien
C&#233;dric Julien

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

Related Questions