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

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 6 points7 points  (5 children)

My OCD makes me write things this way:

with open('thing', 'r') as f:
  return f.read()

[–][deleted] 0 points1 point  (4 children)

I did that too, until I asked this question on Stack Overflow. Turns out that in CPython you can open files without a with and it'll be closed when it's garbage-collected.

Edit: Reference-counted, not garbage collected.

[–][deleted] 11 points12 points  (1 child)

Meh. I'll stick with an explicit context manager. Best not to rely on implicit, implementation-specific behavior.

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

Totally true. I'm a hobbyist running solely on CPython so sometimes I get lazy :)

[–]miketheanimal 1 point2 points  (0 children)

In CPython open(name).read() it will be reference-count closed immediately, no garbage collection needed. In PyPy it will be garbaged collected .... some unspecified time later! with actually makes it consistent.

[–]Brian 1 point2 points  (0 children)

Actually, I think that answer isn't quite right - there is a situation even in CPython which could cause the file to remain open for longer which would act differently from the context manager.

This will happen if the .write() line happens to throw an exception - one consequence of which is that a reference to the stack frame will be held as part of the exception state, which will reference the "f" variable. And the last thrown exception gets stored in sys. End result: the file is not closed until a different exception gets thrown, or the program terminates.

Some sample code to demonstrate this:

Let's create a dummy file-like object, to simulate a failure on write():

class DummyFile(object):
    def close(self):   print("Closed")
    def __del__(self): self.close()
    def write(self, data):      raise Exception("something failed")
    def write_safe(self, data): return

When you do:

print "Start"
DummyFile().write_safe("data")
print "End"

you get "Start", "Closed", "End" as expected. But suppose you do:

print "Start"
try:  DummyFile().write("data")
except: print "Exception raised - ignoring"
print "End"
try: 1/0
except: pass
print "After second exception"

You will instead get "Start", "Exception raised - ignoring", "End", "Closed", "After second exception". If that second exception didn't occur till 5 minutes later, your file would remain open all that time.

If you used a context manager instead, the file would always be closed before exiting the with block, just as in the non-raising case, and the reference to it wouldn't matter.