Understanding the Python 'with' statement

I don't know why no one has mentioned this yet, because it's fundamental to the way with works. As with many language features in Python, with behind the scenes calls special methods, which are already defined for built-in Python objects and can be overridden by user-defined classes. In with's particular case (and context managers more generally), the methods are __enter__ and __exit__.

Remember that in Python everything is an object -- even literals. This is why you can do things like 'hello'[0]. Thus, it does not matter whether you use the file object directly as returned by open:

with open('filename.txt') as infile:
    for line in infile:
        print(line)

or store it first with a different name (for example to break up a long line):

the_file = open('filename' + some_var + '.txt')
with the_file as infile:
    for line in infile:
        print(line)

Because the end result is that the_file, infile, and the return value of open all point to the same object, and that's what with is calling the __enter__ and __exit__ methods on. The built-in file object's __exit__ method is what closes the file.


These behave identically. As a general rule, the meaning of Python code is not changed by assigning an expression to a variable in the same scope.

This is the same reason that these are identical:

f = open("myfile.txt")

vs

filename = "myfile.txt"
f = open(filename)

Regardless of whether you add an alias, the meaning of the code stays the same. The context manager has a deeper meaning than passing an argument to a function, but the principle is the same: the context manager magic is applied to the same object, and the file gets closed in both cases.

The only reason to choose one over the other is if you feel it helps code clarity or style.

Tags:

Python