4

I see regularly this kind of code:

filecontent = open(thefilename).read()

I wonder what becomes the file object created by open in this situation: is it implicitly closed, or does it stay open somewhere ?

michaelmeyer
  • 7,985
  • 7
  • 30
  • 36

1 Answers1

12

I wonder what becomes the file object created by open in this situation: is it implicitly closed, or does it stay open somewhere ?

It stays open until the garbage collector discovers that nobody can access it anymore and destroys it, at which point it is closed by the destructor.

The Python language makes no guarantees as to when that will happen (except, of course, that it will be after you can't access it anymore).

However, the CPython implementation (which you're probably using, because it's the only working 3.x implementation as of May 2013) uses reference counting (plus a cycle detector) for its garbage collection, which means that as soon as the last reference goes away (unless it was involved in a cycle at some point), the object gets destroyed.

So, in CPython, in most cases, the file will get closed as soon as you return from the function, assign a new value to filecontent, or del filecontent. And a lot of quick&dirty code relies on this.

But Jython and IronPython rely on the Java/.NET garbage collector, which periodically checks for garbage in complicated and fancy ways instead of keeping track on the fly, so there is no guarantee as to when anything gets collected. And PyPy has multiple options, depending on how it's configured.

And, even in CPython, it's possible for garbage to stay around after there are no visible references because, e.g., you ran it in the debugger and ended up with some invisible references. Or, if the file was involved in a reference cycle, it may never get closed.

So, you should not rely on this behavior in anything except quick&dirty code. Basically, if it's acceptable that the file stay open until your program finishes, fine. Otherwise, don't do it.

The right way to do this is with a with statement:

with open(thefilename) as f:
    filecontent = f.read()

This guarantees that f.close() gets called as soon as the with statement finishes.

Every so often, people suggest a way to turn this into a one-liner, to which Guido always replies something like, "with open(thefilename) as f: filecontent = f.read() is already a one-liner. And it's kind of bad, but nowhere near as bad as what you're suggesting."

But really, there's an even better answer: Write a function that wraps it:

def read_whole_file(filename):
    with open(thefilename) as f:
        return f.read()

And then:

filecontent = read_whole_file(thefilename)

Clean, concise, readable… there's no excuse for the "open 'em all and let the GC sort 'em out" hack.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 1
    In general, objects are not necessarily collected *at all*. In CPython, a `__del__` method is not called if it's part of a reference cycle - I have no idea if this affects files (I think they are nevertheless closed at interpreter shutdown), but it may matter for our own objects. –  May 06 '13 at 19:42
  • @delnan: Obviously, even if Python abandons a file handle, any modern OS will close it at process shutdown. So, for files in read mode, who cares whether they're closed, right? But for files in _write_ mode, that's a different story, because the file may not get flushed unless it's explicitly closed by the interpreter. See POSIX [`exit`](http://pubs.opengroup.org/onlinepubs/009696799/functions/exit.html) for details. (Not sure where the Windows equivalent is documented.) – abarnert May 06 '13 at 20:55
  • Whoever downvoted, care to explain why? – abarnert Jul 16 '13 at 19:00