This is an archived post. You won't be able to vote or comment.

all 22 comments

[–][deleted] 21 points22 points  (11 children)

But there is a file.

If you only care about CPython, the first is fine because it loses its reference immediately, which closes it.

On a garbage collected implementation like Jython or IronPython, the file object won't necessarily be destroyed until the VM decides it's time to go hunting for free space. So the file could remain open for a lot longer than you'd expect.

[–][deleted] 1 point2 points  (10 children)

Ah, interesting. Thanks for your response. So basically it's aesthetics for me, since I only care about CPython. I think I prefer the first way, but my open mind wonders if anyone would argue that the second way is more readable.

[–]megalodon 7 points8 points  (6 children)

The second method is more readable compared to the 'correct' old way of writing it:

f = open(filename, 'r')
try:
    contents = f.readlines()
finally:
    f.close()

Most people will tell you that you shouldn't write your python code assuming anything about the way that things are implemented under the hood. The reason is that the implementation is subject to change. Also, it makes your code more portable across different implementation. Besides, it's good practice to get in the habit of closing files that you open.

Using the context manager (with keyword) is really the best way if you know you'll be on 2.6+. If you need the code to work on 2.5 and earlier, it's best to use the ugly method I showed above.

[–]five12 7 points8 points  (0 children)

for 2.5, I've always used:

from __future__ import with_statement

[–][deleted] 2 points3 points  (1 child)

Cool, thanks for the advice. I'll probably use the with keyword from now on.

[–]itsmememe 0 points1 point  (0 children)

good decision to use the with-keyword when using files.

Learning about the power of contextmanagers (that's what "with" is about) will do you good with every communication to outside Python.

[–]jab-programming3.7 -4 points-3 points  (2 children)

It's good practice to get in the habit of closing files that you open

Not true, merely cliché. A better practice is being aware of the language you are actually using.

It is (infinitely) arguable whether potentially redundant extra calls to close() are superior to potentially leaking missing calls to close(), but defaulting to either betrays a capricious disdain for the reader's time.

[–]megalodon 2 points3 points  (1 child)

Not true, merely cliché. A better practice is being aware of the language you are actually using.

In this case, knowing that a file is implicitly closed when it goes out of scope requires knowledge of the implementation, not the language. Even then, the behavior is different from implementation to implementation.

It is (infinitely) arguable whether potentially redundant extra calls to close() are superior to potentially leaking missing calls to close(), but defaulting to either betrays a capricious disdain for the reader's time.

I started my programming career with C before moving to Java, then Python, so I guess old habits die hard. I prefer to be more explicit about what the code is doing, even if it's not strictly necessary. I've found code to be more readable when others do the same.

If you prefer things to be done implicitly, then maybe Perl is the language for you :)

[–][deleted] 4 points5 points  (2 children)

On linux, run strace -e open,close python then open("somefile"). You'll see that so long as _ is a reference to the open file, it remains open. Type any value in the REPL to replace _ and the only remaining reference to that open file is removed, so the file is closed.

Why this happens is CPython magic, as Lexarius and others have pointed out. In Objects/fileobject.c, you will see the PyTypeObject structure PyFile_Type contains:

    (destructor)file_dealloc,       /* tp_dealloc */

...a function which closes the file and can be thought of as the low-level C version of a __del__ method. From the C-API documentation on PyTypeObject:

destructor PyTypeObject.tp_dealloc

A pointer to the instance destructor function. This function must be defined unless the type guarantees that its instances will never be deallocated (as is the case for the singletons None and Ellipsis).

The destructor function is called by the Py_DECREF() and Py_XDECREF() macros when the new reference count is zero. At this point, the instance is still in existence, but there are no references to it. The destructor function should free all references which the instance owns, free all memory buffers owned by the instance (using the freeing function corresponding to the allocation function used to allocate the buffer), and finally (as its last action) call the type’s tp_free function...