Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After passing open call as argument to another function, is the file object closed?

Tags:

python

If I want to use a file object just once, normally I would still use the with block or explicitly close the file object when I'm done, because closing a file seems to be the right-thing-to-do:

with open('filename','r') as f:
    x = dosomething(f)

or

f = open('filename','r')
x = dosomething(f)
f.close()

However, I've seen people use pass the call to open directly to a function without saving the output to any variable, making it impossible to close explicitly:

x = dosomething(open('filename','r'))

So, is either of these true,

  1. Somehow the unnamed file object gets closed
  2. It doesn't matter, it's fine not to close the file

or is this a bad practice?

Also, does the answer change if I allow the file to be read/write?

To be clear, dosomething could be something like np.array() or for i in f.readlines()

like image 686
askewchan Avatar asked Oct 15 '13 02:10

askewchan


1 Answers

It's a poor practice. It has nothing to do with the open mode (read, write, read+write, append, binary mode, text mode ...).

In CPython, "it almost always works" because file objects are automatically closed when they become garbage (unreachable), and CPython's reference counting usually collects (non-cyclic) garbage very soon after it becomes garbage.

But relying on that is indeed poor practice.

If, for example, dosomething(f) runs for an hour before it returns, chances are excellent the file object will remain open the entire time.

Note: in some cases, dosomething(f) may be coded to explicitly close the passed-in file object itself. In those cases, it's not poor practice ;-)

Later: a related thing I've often seen is this:

data = open(file_path).read()

In CPython, the unnamed file object is garbage-collected (and so also closed) immediately when the statement completes, thanks to CPython's reference-counting. People are then surprised when they move their code to a different Python implementation, and get OS "too many open files!" complaints. Heh - serves 'em right ;-)

Example:

open("Output.txt", "w").write("Welcome")
print open("Output.txt").read()

This prints

Welcome

in CPython, because the unnamed file object from the first statement is garbage-collected (and closed) immediately when the first statement ends.

But:

output = open("Output.txt", "w")
output.write("Welcome")
print open("Output.txt").read()

probably prints an empty line in CPython. In this case, the file object is bound to a name (output), so is not garbage collected when the second statement completes (a smarter implementation could theoretically detect that output is never used again, and garbage-collect right away, but CPython does not).

"Welcome" is probably still in the file's memory buffer, so is not yet written to disk. So the third statement probably finds an empty file, and prints nothing.

In other implementations of Python, both examples may well print empty lines.

like image 155
Tim Peters Avatar answered Sep 20 '22 07:09

Tim Peters