Reading and Writing Files
Variables die with the program. Files are how data survives — and Python’s with open(...) pattern makes reading and writing them one of the cleanest parts of the language.
Writing a file
with open("notes.txt", "w") as f:
f.write("Line one\n")
f.write("Line two\n")
print("saved!")
Decode the line:
open("notes.txt", "w")— open the file in mode"w"(write)as f— the open file is calledfinside the blockf.write("...")— write text (no automatic newline — hence the\ns)with— the magic: when the block ends, the file is closed automatically, even if an error happened
Always use with. Manual open()/close() works but forgetfulness causes real data loss; with makes closing impossible to forget.
The modes (there are only three you need)
| Mode | Name | Behavior |
|---|---|---|
"w" |
write | Erases the file, then writes fresh |
"a" |
append | Keeps existing content, adds at the end |
"r" |
read | Read only (the default) |
The "w" erasure surprises people:
with open("notes.txt", "w") as f:
f.write("just this")
# notes.txt now contains ONLY "just this" — previous content gone!
Adding to a log or history? "a". Creating a fresh report? "w".
Reading a file — three ways
with open("notes.txt", "r") as f:
# Way 1: everything as one string
content = f.read()
print(content)
with open("notes.txt") as f: # "r" is the default
# Way 2: list of lines (newlines included)
lines = f.readlines()
print(lines) # ['Line one\n', 'Line two\n']
# Way 3: line by line — the memory-friendly way
with open("notes.txt") as f:
for line in f:
print(line.strip()) # strip removes the trailing \n
Way 3 is the professional default — it processes one line at a time, so even a million-line file never overloads memory. The .strip() in the loop matters: each line arrives with its invisible \n attached.
The classic combo: write then read back
# Save
with open("highscore.txt", "w") as f:
f.write("1250")
# Later... load
with open("highscore.txt") as f:
score = int(f.read().strip())
print(f"High score: {score}") # High score: 1250
Notice int() — files give back strings, always. Numbers round-trip through str() and int().
FileNotFoundError — your first handled file error
try:
with open("ghost.txt") as f:
print(f.read())
except FileNotFoundError:
print("That file doesn't exist!")
Reading a missing file raises FileNotFoundError — wrap it when the file might legitimately be absent (first run of an app, optional config).
Common Errors & Fixes
FileNotFoundError— wrong filename or wrong folder. Files open relative to where you ran the script, not where the script lives. Use full paths or run from the right directory.- File is empty after writing — you forgot
f.write()entirely, or wrote after thewithblock closed. io.UnsupportedOperation: not readable— opened with"w"(write-only) and tried to read. Reopen with"r"or"w+".- Weird
\nat the end of every line — that’s the line’s real newline;.strip()it.
✅ Checkpoint
- Which mode erases the file first? (“w”)
- Why
withinstead of open/close? (Auto-closes even on errors — can’t forget) - What does each line from a for-loop read arrive with? (A trailing \n — strip it)
- What type is everything read from a file? (String — convert numbers yourself)
Next: JSON and CSV — the two file formats that power real applications.