6

I have a class that opens a file for writing. In my destructor, I call the function that closes the file:

class MyClass: def __del__(self): self.close() def close(self): if self.__fileHandle__ is not None: self.__fileHandle__.close() 

but when I delete the object with code like:

myobj = MyClass() myobj.open() del myobj 

if I try to reinstantiate the object, I get a value error:

ValueError: The file 'filename' is already opened. Please close it before reopening in write mode. 

whereas if I call myobj.close() before del myobj I don't get this problem. So why isn't __del__() getting called?

4
  • 1
    Try inheriting from object - not doing so has been considered out-of-date style for some six years. Commented Jan 18, 2012 at 9:37
  • You should show some more code. How can one tell you how myobj.close() changes things, without knowing what it does? Commented Jan 18, 2012 at 9:37
  • 1
    Keep in mind that del doesn't call __del__. It just removes one of the references. Generally it's better to close explicitly, possibly using the context manager protocol (with statement). Commented Jan 18, 2012 at 9:45
  • 1
    @Marcin - that solved it ... post as an answer? Still curious as to why this should be the case ... Commented Jan 18, 2012 at 9:47

4 Answers 4

8

Are you sure you want to use __del__? There are issues with __del__ and garbage collection.

You could make MyClass a context manager instead:

class MyClass(object): def __enter__(self): return self def __exit__(self,ext_type,exc_value,traceback): if self.__fileHandle__ is not None: self.__fileHandle__.close() 

By doing so, you could use MyClass like this:

with MyClass() as myobj: ... 

and myobj.__exit__ (and thus self.__fileHandle__.close()) will be called when Python leaves the with-block.

Sign up to request clarification or add additional context in comments.

3 Comments

+1 for mentioning the issues. docs.python.org/library/gc.html#gc.garbage says that «Objects that have __del__() methods and are part of a reference cycle cause the entire reference cycle to be uncollectable, including objects not necessarily in the cycle but reachable only from it.»
I don't see how there's a reference cycle in the OP's example. There's one object, it gets del'd, and __del__() doesn't get called.
Also, in my situation, I'd rather let the GC handle stuff as it doesn't need to all get closed immediately, just eventually... but it always has problems. And no, context managers are not a real solution. They restrict you to closing the object within the same function call and indent your code one level every time you use one. Makes it unreadable when I want to create 20 of these.
8

That's not what del does. It's unfortunate that __del__ has the same name as del, because they are not related to each other. In modern terminology, the __del__ method would be called a finalizer, not a destructor and the difference is important.

The short difference is that it's easy to guarantee when a destructor is called, but you have very few guarantees about when __del__ will be called and it might never be called. There are many different circumstances that can cause this.

If you want lexical scoping, use a with statement. Otherwise, call myobj.close() directly. The del statement only deletes references, not objects.

I found another answer (link) to a different question that answers this in more detail. It is unfortunate that the accepted answer to that question contains egregious errors.

Edit: As commentors noted, you need to inherit from object. That is fine, but it is still possible that __del__ will never be called (you could be getting lucky). See the linked answer above.

Comments

2

Your code should inherit from object - not doing so has been considered out of date (except in special cases) for at least six years.

You can read about __del__ here: http://docs.python.org/reference/datamodel.html#object.del

The short version of why you need to inherit from object is that __del__ is only "magic" on new-style classes.

If you need to rely on calling of a finalizer, I strongly suggest that you use the context manager approach recommended in other answers, because that is a portable, robust solution.

2 Comments

Ah just found an instance where this "magic" doesn't work ... using ipython instead.
@tdc: Unless I'm mistaken, ipython is based on cpython. In any case, if you rely on the behaviour of a particular instance, your code will be non-portable. It would probably be better to use the context manager approach recommended in other answers.
0

Perhaps something else is referencing it, which is why __del__ isn't being called (yet).

Consider this code:

#!/usr/bin/env python import time class NiceClass(): def __init__(self, num): print "Make %i" % num self.num = num def __del__(self): print "Unmake %i" % self.num x = NiceClass(1) y = NiceClass(2) z = NiceClass(3) lst = [x, y, z] time.sleep(1) del x del y del z time.sleep(1) print "Deleting the list." del lst time.sleep(1) 

It doesn't call __del__ of NiceClass instances until we delete the list that references them.

Unlike C++, __del__ isn't being called unconditionally to destruct the object on demand. GC makes things a bit harder. Here is some info: http://arctrix.com/nas/python/gc/

1 Comment

There was definitely only one reference in this case

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.