File I/O and context managers
Opening a file, and the cleanup problem
open() gives you a file object — a handle to the actual file on
disk, not the file’s contents directly:
"r" is the mode:
"r"for reading (the default if omitted)"w"for writing (creates the file if it doesn’t exist, and overwrites it completely if it does)"a"for appending (writes get added to the end, the existing content is left alone)
f.close() releases the underlying operating system resource — skipping
it isn’t just untidy, it can leave a file locked or its contents not
fully flushed to disk.
The problem: f.close() only runs if execution actually reaches that
line. If something raises an exception in between open() and close(),
the file never gets closed — the same
line-by-line execution model from the Python setup lesson
means Python just stops wherever the error happened:
with — guaranteed cleanup, even on exception
You’ve already seen the fix for “cleanup that must happen no matter what”
—
finally, from the error handling concept back in the Python setup lesson.
File handling has dedicated syntax for exactly this pattern, since it’s
common enough to deserve its own shorthand: with.
The ZeroDivisionError still happens and still propagates — with
doesn’t swallow exceptions — but the file is guaranteed to be closed
before that traceback ever reaches you, whether the block finishes
normally or crashes partway through. f is only valid inside the
indented block; there’s no f.close() to remember or forget. This is
Python’s version of Java’s try-with-resources — a dedicated syntax for
“acquire something, guarantee its cleanup,” rather than relying on a
programmer to remember a matching finally.
open() returning something usable with with is what makes it a
context manager — an object that defines what should happen on entry
(here, nothing extra — the file’s already open) and on exit (closing the
file), regardless of how the block ends. You won’t write your own context
manager in this course, but recognizing the pattern matters: any object
usable after with ... as ...: is promising the same guarantee with open(...) gives you.
Reading a file: four ways, and when each fits
.read() loads the entire file into memory as a single string, newlines
and all — fine for small files, wasteful for anything large.
.readline() reads exactly one line at a time, remembering where it left
off between calls — useful when you need fine control over reading one
line, checking it, and deciding whether to read further.
.readlines() reads the whole file at once, same as .read(), but
returns a list of lines instead of one string — still loads everything
into memory up front.
Directly iterating the file object with for line in f: is the most
idiomatic option for processing a file line by line — a file object is
itself an
iterable, in exactly the sense covered back in the control flow concept,
handing out one line at a time without ever loading the whole file into
memory at once. For a genuinely large file, this matters: .read()/
.readlines() on a multi-gigabyte file can exhaust memory; the for
version never holds more than one line at a time.
Since a file object is an iterator, next() works on it directly too,
pulling one line at a time by hand — the same mechanism the for loop
uses internally, just invoked one step at a time yourself:
Writing to a file
Writing uses "w" or "a" mode and .write(), which — unlike print()
— doesn’t add a newline automatically:
Opening the same file again with "w" overwrites it completely — worth
being careful about, since there’s no confirmation or undo:
"a" mode instead adds to the end without touching what’s already there:
.seek() and .tell() — moving around inside a file
A file object tracks its current read/write position internally — every
.read(), .readline(), or next() call advances it. .tell() reports
the current position (in bytes); .seek() jumps to a specific position
directly:
f.seek(0) is the common case — rewinding to re-read a file from the
start within the same with block, without closing and reopening it.
What does open("file.txt", "w") do if file.txt already exists and already has content?