File I/O

Python Intermediate — File I/O
Python Intermediate
Course 2 · Chapter 4 · File I/O

📁 File I/O

Everything so far has lived only in memory while the program runs. This chapter covers reading and writing real files: open() and its modes, the with statement (a context manager) that guarantees files get closed properly, and pathlib — the modern, object-oriented way to work with file paths.

📖 Opening and Reading Files

file = open("notes.txt", "r") # "r" = read mode (the default) contents = file.read() # the entire file, as one string file.close() # must close manually — see the warning below file = open("notes.txt", "r") lines = file.readlines() # a list of strings, one per line file.close() file = open("notes.txt", "r") for line in file: # iterate line by line — memory-efficient for large files print(line.strip()) # .strip() removes the trailing newline file.close()

🔒 The with Statement (Context Managers)

with open("notes.txt", "r") as file: contents = file.read() # file is automatically closed here, even if an exception occurred inside the block

with is a context manager — it guarantees cleanup code runs when the block exits, whether that's normally or via an exception, the same guarantee finally gave you in the last chapter. For files specifically, this is the idiomatic form; plain open()/.close() is considered old-fashioned in real code.

⚠ Forgetting .close() Leaks a File Handle

If an exception happens between a manual open() and its matching .close(), the close() line is skipped entirely — the file stays open, holding onto a system resource, for as long as the program keeps running. with closes the file automatically no matter how the block exits, which is why it's always preferred over manual open/close pairs.

✏️ Writing Files

with open("log.txt", "w") as file: # "w" = write mode file.write("First line\n") file.write("Second line\n") with open("log.txt", "a") as file: # "a" = append mode file.write("Third line\n")
⚠ "w" Mode Erases the File's Existing Contents Immediately

Opening a file in "w" mode truncates it to empty the instant it's opened — before you've written anything new. Opening an existing file you meant to add to using "w" instead of "a" silently destroys everything already in it. Double-check which mode you actually want before running write code against a real file.

🗂️ pathlib

pathlib represents file paths as objects rather than plain strings, with cross-platform path joining and useful methods built in:

from pathlib import Path data_dir = Path("data") file_path = data_dir / "notes.txt" # the / operator joins paths — works on Windows AND Linux print(file_path.exists()) # True/False — no need to open() just to check print(file_path.name) # notes.txt print(file_path.suffix) # .txt contents = file_path.read_text() # reads the whole file — no open()/close() needed file_path.write_text("new contents") # writes the whole file in one call

Path's / operator is a genuinely nice piece of operator overloading — it replaces manually concatenating strings with slashes (which breaks on Windows anyway, since Windows uses backslashes) with something that reads naturally and works everywhere.

Guaranteed Cleanup: Go vs Kotlin vs Python

LanguageMechanism
Godefer file.Close() — scheduled to run when the enclosing function returns, regardless of how it returns (including via an error).
Kotlinfile.bufferedReader().use { ... } — the use function is Kotlin's own context-manager equivalent, closing the resource automatically at the end of the block.
Pythonwith open(...) as file: — the file closes automatically when the with block ends.

All three languages converged on the same underlying idea — guaranteed cleanup tied to a block's exit, not left to manual discipline — just with different syntax and a different name for the concept.

open() modes

"r" read, "w" write (truncates!), "a" append.

with statement

Guarantees the file closes automatically, even if an exception occurs.

Reading

.read() (whole file), .readlines() (list of lines), or iterate directly.

pathlib

Path objects, / for joining, .read_text()/.write_text() shortcuts.

💻 Coding Challenges

Challenge 1: Write Then Read

Using with and "w" mode, write three lines to a file called output.txt. Then, in a separate with block using "r" mode, read and print each line (with .strip() to remove trailing newlines).

Goal: Practice the write-then-read round trip using context managers for both.

→ Solution

Challenge 2: Line Counter

Given a text file with several lines, write a function count_lines(filename) that opens it with with and returns how many lines it contains, by iterating over the file object directly rather than calling .readlines().

Goal: Practice line-by-line iteration over a file object as a memory-efficient alternative to reading everything into a list.

→ Solution

Challenge 3: pathlib File Check

Using pathlib, write code that checks whether a file called config.txt exists in a folder called settings. If it exists, print its .suffix; if not, use .write_text() to create it with the contents "default config".

Goal: Practice combining Path's / operator, .exists(), and .write_text() in one realistic check-then-act flow.

→ Solution

🎯 What's Next

Next chapter: Comprehensions & Generators — list/dict/set comprehensions in depth, generator functions, and yield.