Lesson 15 — Files

File Handling

Python file handling uses the with statement for automatic resource management. The csv module and pathlib make common tasks clean and cross-platform.

PYTHON — Writing files
# Writing — "w" creates or overwrites:
with open("attendance.txt", "w") as f:
    f.write("Monday:    28 learners present\n")
    f.write("Tuesday:   31 learners present\n")

# Appending — "a" adds without erasing:
with open("attendance.txt", "a") as f:
    f.write("Wednesday: 27 learners present\n")

# Writing multiple lines at once:
lines = [
    "Thursday:  30 learners present",
    "Friday:    25 learners present"
]
with open("attendance.txt", "a") as f:
    f.writelines(line + "\n" for line in lines)
PYTHON — Reading files
# Reading entire file:
with open("attendance.txt") as f:
    content = f.read()
print(content)

# Reading line by line (memory efficient for large files):
with open("attendance.txt") as f:
    for line_num, line in enumerate(f, start=1):
        print(f"{line_num:3}: {line.strip()}")

# Reading all lines into a list:
with open("attendance.txt") as f:
    lines = f.readlines()   # includes \n
lines = [line.strip() for line in lines]  # remove whitespace

CSV Files

PYTHON — CSV files
import csv

# Writing CSV:
learners = [
    ["name",    "course",   "mark"],  # header
    ["Thandi",  "Java",     74.5],
    ["Sipho",   "Python",   81.0],
    ["Lerato",  "SQL",      68.0]
]

with open("learners.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(learners)

# Reading CSV as dicts (column name → value):
with open("learners.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        name   = row["name"]
        course = row["course"]
        mark   = float(row["mark"])  # DictReader gives strings
        print(f"{name} | {course} | {mark:.1f}%")

pathlib — Modern File Paths

PYTHON — pathlib
from pathlib import Path

# Cross-platform paths (no hardcoded slashes):
data_dir = Path("data")
data_dir.mkdir(exist_ok=True)     # create if not exists

log_file = data_dir / "log.txt"   # / operator joins paths
log_file.write_text("Started.")   # one-line write
print(log_file.read_text())        # one-line read

# File info:
print(log_file.exists())           # True
print(log_file.stat().st_size)     # size in bytes
print(log_file.suffix)             # .txt

# List all CSV files in a directory:
for csv_path in data_dir.glob("*.csv"):
    print(csv_path.name)

Practice Task

Your Turn

Write a LearnerRegistry class that: saves a list of Learner objects to a CSV file with headers, loads them back and reconstructs Learner objects, handles FileNotFoundError with a clear message, handles corrupt/missing fields gracefully. Test by saving 5 learners, clearing the list, reloading, and printing each.

Common Mistakes

  • Mode 'w' erases existing content — use 'a' for appending.
  • Not stripping newlines when reading — line.strip() removes leading/trailing whitespace.
  • Always use with open() — bare open() without closing can leak file handles.
  • Forgetting newline='' when writing CSV on Windows — causes blank rows.
  • DictReader gives strings for all values — explicitly convert with int() or float().

Professional Tip

Use pathlib for new code — it is object-oriented, cross-platform, and more readable than os.path. The / operator for path joining is one of Python's cleverest overloads.

Mini Quiz

Which file mode appends to an existing file without erasing it?