If you just fire up Python and use either of those options, the net effect is the same if the base instance of Python's file
object is not changed. (In Option One, the file is only closed when file_obj
goes out of scope vs at the end of the block in Option Two as you have already observed.)
There can be differences with use cases with a context manager however. Since file
is an object, you can modify it or subclass it.
You can also open a file by just calling file(file_name)
showing that file
acts like other objects (but no one opens files that way in Python unless it is with with
):
>>> f=open('a.txt')
>>> f
<open file 'a.txt', mode 'r' at 0x1064b5ae0>
>>> f.close()
>>> f=file('a.txt')
>>> f
<open file 'a.txt', mode 'r' at 0x1064b5b70>
>>> f.close()
More generally, the opening and closing of some resource called the_thing
(commonly a file, but can be anything) you follow these steps:
set up the_thing # resource specific, open, or call the obj
try # generically __enter__
yield pieces from the_thing
except
react if the_thing is broken
finally, put the_thing away # generically __exit__
You can more easily change the flow of those subelements using the context manager vs procedural code woven between open
and the other elements of the code.
Since Python 2.5, file objects have __enter__ and __exit__ methods:
>>> f=open('a.txt')
>>> f.__enter__
<built-in method __enter__ of file object at 0x10f836780>
>>> f.__exit__
<built-in method __exit__ of file object at 0x10f836780>
The default Python file
object uses those methods in this fashion:
__init__(...) # performs initialization desired
__enter__() -> self # in the case of `file()` return an open file handle
__exit__(*excinfo) -> None. # in the case of `file()` closes the file.
These methods can be changed for your own use to modify how a resource is treated when it is opened or closed. A context manager makes it really easy to modify what happens when you open or close a file.
Trivial example:
class Myopen(object):
def __init__(self, fn, opening='', closing='', mode='r', buffering=-1):
# set up the_thing
if opening:
print(opening)
self.closing=closing
self.f=open(fn, mode, buffering)
def __enter__(self):
# set up the_thing
# could lock the resource here
return self.f
def __exit__(self, exc_type, exc_value, traceback):
# put the_thing away
# unlock, or whatever context applicable put away the_thing requires
self.f.close()
if self.closing:
print(self.closing)
Now try that:
>>> with Myopen('a.txt', opening='Hello', closing='Good Night') as f:
... print f.read()
...
Hello
[contents of the file 'a.txt']
Good Night
Once you have control of entry and exit to a resource, there are many use cases:
- Lock a resource to access it and use it; unlock when you are done
- Make a quirky resource (like a memory file, database or web page) act more like a straight file resource
- Open a database and rollback if there is an exception but commit all writes if there are no errors
- Temporarily change the context of a floating point calculation
- Time a piece of code
- Change the exceptions that you raise by returning
True
or False
from the __exit__ method.
You can read more examples in PEP 343.