Lesson 20: File Handling & Context Managers
Read and write files safely with the with statement, work with paths, and handle CSV data.
Files — Talking to the Disk
Programs that outlive a single run need persistence — the ability to save data and read it back later. Files store data between runs: reading config, saving results, processing CSV exports. Without file handling, every program forgets everything when it closes.
Everything from a saved game to a bank statement is just bytes in a file. Python's file tools are the gateway to the disk — and they are surprisingly easy once you master the with pattern.
What You'll Learn in This Lesson
- Open files safely with the
withstatement - Read files line by line, chunk by chunk
- Write and append data
- Handle CSV tabular data with the
csvmodule - Work with paths using
pathlib
The with Statement — Safe & Automatic
with guarantees the file is closed even if an error occurs mid-operation:
with open("students.txt", "w", encoding="utf-8") as f:
f.write("Amol,Python\n")
# File is automatically closed here — even if an error happened above
Never use bare open() without closing — a leaked open file handle can corrupt data or exhaust system resources. The with block is the professional standard.
Mental model: the
withblock is a safety bubble. When the bubble pops (normally or via an error), the file is closed automatically.
What's really happening? The file object implements the context manager protocol — __enter__ (opened for you) and __exit__ (always called when the block ends, even on exceptions). That __exit__ is what guarantees the cleanup.
File Modes
| Mode | Meaning | Behavior |
|---|---|---|
"r" |
Read | File must exist; read-only |
"w" |
Write | Overwrites the whole file |
"a" |
Append | Adds to the end, keeps existing |
"r+" |
Read + write | Both, file must exist |
"rb" / "wb" |
Binary | Images, audio, pickles |
Warning: "w" erases the file contents the moment you open it. If you want to keep existing data, use "a" (append).
The most common beginner data-loss bug: opening with
"w"when you meant"a". The file is truncated the instantopen()succeeds — before a single line is written. Always double-check your mode.
Reading Patterns
| Pattern | What you get | Best for |
|---|---|---|
f.read() |
The whole file as one string | Small files |
f.readline() |
One line at a time | Processing in order |
for line in f: |
Iterates lines lazily | Big files — memory friendly |
f.readlines() |
List of all lines | When you need the list |
with open("students.txt", "r", encoding="utf-8") as f:
for line in f: # never loads the whole file
print("Read:", line.strip())
For a 10 GB log file, for line in f: streams it line by line using almost no memory.
Mental model:
for line in fis a lazy conveyor belt — one line exists at a time.f.read()is a forklift that picks up the entire warehouse at once. Choose the tool that matches your file size.
pathlib — Modern Path Handling
The old os.path module is clunky. pathlib gives you clean, cross-platform path objects:
from pathlib import Path
data_dir = Path("data")
data_dir.mkdir(exist_ok=True) # create folder if missing
file_path = data_dir / "students.txt" # / joins paths cleanly!
print(file_path.exists()) # True / False
print(file_path.name) # students.txt
print(file_path.suffix) # .txt
print(file_path.read_text()) # read whole file as text
file_path.write_text("hello\n") # write text
No more os.path.join string gymnastics — / does the joining, and it works identically on Windows and macOS/Linux.
CSV — Tabular Files
The csv module parses commas safely — handling quoted fields, escaping, and newlines inside fields:
import csv
# Write
with open("students.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["name", "course"])
writer.writerow(["Amol", "Python"])
# Read
with open("students.csv", "r", encoding="utf-8") as f:
rows = list(csv.reader(f))
print(rows) # [['name', 'course'], ['Amol', 'Python']]
Always pass
newline=""when writing CSVs — otherwise you get blank lines between rows on Windows.
csv.DictReader — CSV with headers as dicts:
with open("students.csv", "r", encoding="utf-8") as f:
for row in csv.DictReader(f):
print(row["name"], row["course"]) # access by header name
Once a CSV row is a dict, every lesson from Module 3 applies: .get(), comprehensions, grouping — instant data workflow.
Common Mistakes to Avoid
- Mistake: Opening a file for
"w"when you meant to append — Fix: use"a"to keep existing data. - Mistake: Forgetting
encoding="utf-8"and getting mojibake — Fix: always specify the encoding. - Mistake: Reading a file without
withand leaking handles — Fix: always use thewithstatement. - Mistake:
f.read()on a giant file and running out of memory — Fix: iterate withfor line in f. - Mistake: Forgetting
newline=""when writing CSVs — Fix: pass it on every CSV write.
Professional Tips & Tricks
- Always specify
encoding='utf-8'— avoids cross-platform encoding bugs. - Never open without
with— a leaked open file handle can corrupt data or exhaust resources. - Use
pathlib.Pathfor modern, cross-platform path handling. - Use
csv.DictReaderto turn rows into dicts instantly. - Check
Path.exists()before reading to avoidFileNotFoundError(or catch it — Lesson 21).
Key Takeaways
with open(...) as f:closes files automatically — always use it."r"read,"w"write (overwrites!),"a"append.for line in f:streams big files memory-efficiently.- The
csvmodule handles tabular data safely. - Always pass
encoding="utf-8"andnewline=""for CSVs. pathlib.Pathmakes path handling clean and cross-platform.
Next up: Exception handling — making your programs fail gracefully.
import csv
# Write data to a file
with open("students.txt", "w", encoding="utf-8") as f:
f.write("Amol,Python\n")
f.write("Riya,AI\n")
# Read it back line by line
with open("students.txt", "r", encoding="utf-8") as f:
for line in f:
print("Read:", line.strip())
# Work with CSV properly
with open("students.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["name", "course"])
writer.writerow(["Amol", "Python"])
writer.writerow(["Riya", "Data Science"])
with open("students.csv", "r", encoding="utf-8") as f:
rows = list(csv.reader(f))
print("CSV rows:", rows)Lesson Code (Python)
import csv
# Write data to a file
with open("students.txt", "w", encoding="utf-8") as f:
f.write("Amol,Python\n")
f.write("Riya,AI\n")
# Read it back line by line
with open("students.txt", "r", encoding="utf-8") as f:
for line in f:
print("Read:", line.strip())
# Work with CSV properly
with open("students.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["name", "course"])
writer.writerow(["Amol", "Python"])
writer.writerow(["Riya", "Data Science"])
with open("students.csv", "r", encoding="utf-8") as f:
rows = list(csv.reader(f))
print("CSV rows:", rows)Console Output
Read: Amol,Python
Read: Riya,AI
CSV rows: [['name', 'course'], ['Amol', 'Python'], ['Riya', 'Data Science']]Code Visualization Tips
- Picture the file as a tape: each read/write moves the head forward.
- Trace write mode as 'erase the tape, then record' — that is why 'w' overwrites.
- Visualize the with block as a safety bubble that automatically closes the file when popped.
Professional Tips & Tricks
- Always specify encoding='utf-8' — avoids cross-platform encoding bugs.
- Never open without with — a leaked open file handle can corrupt data or exhaust resources.
- Use pathlib.Path for modern, cross-platform path handling.
Python Code Judge & Practice Arena
LeetCode StyleRun real Python 3.12 WebAssembly code directly in your browser against automated test suites.
Problem 1: CSV Line Parser 1
Test Your Knowledge
Instant feedbackQuick Check: Lesson 20: File I/O, Context Managers & CSV Processing
Up next · Continue learning
Exception Handling — Fail Gracefully
try/except/else/finally, raising your own errors, and writing code that never crashes the user.