Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the file closed? [duplicate]

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 ?

like image 200
michaelmeyer Avatar asked Dec 16 '22 11:12

michaelmeyer


1 Answers

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.

like image 87
abarnert Avatar answered Dec 18 '22 11:12

abarnert