LambdaLabTM
Computer Science · Class 12 · Text Files
Text fileswith⏱️ 11 min read

Opening a File with

The last lesson ended with seven lines of try and finally wrapped around two lines of actual work, all so that one file would definitely get closed. Python has a statement that does the whole of that, and it is four letters long.

1The shape of a with block

with open('notes.txt', 'r') as f:
everything indented under it

is where the file is open. The moment the indenting stops, the file is closed.

Read it as a sentence: with this file open as f, do this. The colon and the indented block are the same ones you write after if and for — nothing new there.

with_block.py
with open('notes.txt', 'r') as f:
    print(f.read())
    print('inside the block, closed?', f.closed)

print('after the block, closed?', f.closed)
Output
Python is easy to learn.
A file keeps your data safe.
Practice every day.

inside the block, closed? False
after the block, closed? True
Key Takeaway
Nobody wrote f.close(), and the file is closed. That is the entire point of the statement: the block closes the file on the way out, whichever way out it takes.

2The same program, both ways

the long way
f = open('notes.txt', 'r')
try:
    print(f.read())
finally:
    f.close()

Five lines, of which one does the work.

the with way
with open('notes.txt', 'r') as f:
    print(f.read())

Two lines, and the closing is not something you can forget.

3The case that matters: a crash inside the block

Shorter is not the reason to use it. This is. Both programs below break in the middle, and only one of them leaves the file shut:

plain_crash.py
try:
    f = open('notes.txt', 'r')
    print('the file is open')
    print(10 / 0)
    f.close()
except ZeroDivisionError:
    print('The program crashed before close() ran.')

print('closed?', f.closed)
Output
the file is open
The program crashed before close() ran.
closed? False
with_crash.py
try:
    with open('notes.txt', 'r') as f:
        print('the file is open')
        print(10 / 0)
except ZeroDivisionError:
    print('The program crashed inside the block.')

print('closed?', f.closed)
Output
the file is open
The program crashed inside the block.
closed? True
with does not catch anything
Look again at the second program: the try is still there, and it is still doing the catching. with handles closing, not errors. Take the try away and the ZeroDivisionError ends the program exactly as before — with the file neatly closed on the way out.

The same goes for a file that is not there. with cannot help with that either, because the failure happens in open(), before there is anything to close:

with_missing.py
with open('marks.txt', 'r') as f:
    print(f.read())
Output
Traceback (most recent call last):
  File "with_missing.py", line 1, in <module>
    with open('marks.txt', 'r') as f:
         ^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'marks.txt'

Which means the two work together, and this is the shape most real programs end up with:

both.py
try:
    with open('marks.txt', 'r') as f:
        print(f.read())
except FileNotFoundError:
    print('marks.txt is not in this folder.')
Output
marks.txt is not in this folder.

4f still exists afterwards — and is closed

The name f does not disappear at the end of the block. It is an ordinary variable holding a now-closed file object, so using it is the ValueError from the last lesson:

after_the_block.py
with open('notes.txt', 'r') as f:
    data = f.read()

print(data)
print(f.read())
Output
Python is easy to learn.
A file keeps your data safe.
Practice every day.

Traceback (most recent call last):
  File "after_the_block.py", line 5, in <module>
    print(f.read())
          ^^^^^^^^
ValueError: I/O operation on closed file.
Read what you need inside the block
Look at what the program got away with and what it did not. data was filled inside the block, so printing it afterwards is fine — the string is just a string. Reading f again afterwards is not: that needs the file, and the file is shut.

5Two files in one with

Copying, filtering, splitting a file — all of them need two files open together. Separate them with a comma and both are closed at the end of the block:

copying.py
with open('notes.txt', 'r') as source, open('copy.txt', 'w') as target:
    target.write(source.read())

print(open('copy.txt').read())
Output
Python is easy to learn.
A file keeps your data safe.
Practice every day.

6Try it

with_it.py
📄 notes.txt

7Recap

with open(name, mode) as f:

Opens the file, puts the handle in f, and runs the indented block with it open.

The block closes the file

At the end of the block, always — normally, on an exception, or on a return out of a function.

No close() to forget

Writing one is not wrong, only pointless. The block will close it again, which is harmless.

It handles closing, not errors

A FileNotFoundError or a crash inside the block still needs try / except around it.

f is closed after the block

The variable survives; the connection does not. Save what you read into another variable inside the block.

Two files, one with

Separate them with a comma. Both are closed at the end.

✍️ Now write these yourself
  1. 1

    Rewrite one of your open/close programs as a with block.

    Hint · Two lines disappear: the close(), and usually the try.

  2. 2

    Print f.closed inside the block and again after it.

    Hint · False inside, True outside — the block did it for you.

  3. 3

    Put a deliberate 10 / 0 inside a with block and check f.closed afterwards.

    Hint · Catch the ZeroDivisionError, or the print never runs.

  4. 4

    Copy every line of one file into another using a single with.

    Hint · Two open() calls, one comma, one block.

Quick Check

What does the with clause do that open() alone does not?

Quick Check

After a with block ends, what is f?

Quick Check

Which of these opens two files in one with statement?

Quick Check

Does a with block need f.close() at the end?