Python File Handling
Tutorial 34 of 65 · pythondeck.com Python course
Use open() with the appropriate mode (r, w, a, x, optional b for binary, + for read/write). Always use a with block so the file is closed automatically, even on exceptions.
Files bridge your program and persistent storage: logs, configs, exports, uploads. Python's model is pathlib-first for paths and built-in open() for byte or text streams. Text mode handles encoding; binary mode preserves raw bytes for images, PDFs, and network payloads.
Always close files or use context managers so descriptors are released on success and failure. On Windows, locking and path separators differ from POSIX—pathlib normalises most pain.
Text vs binary mode: "r", "w", "a", "rb", "wb".
Encoding argument: encoding="utf-8" for text files cross-platform.
Context managers: with open(path) as f: auto-closes even on exceptions.
pathlib.Path for joining, existence, suffix, and home-relative paths.
File-like objects: io.StringIO, network streams, and the file API contract.
OS limits: permissions, read-only media, and concurrent access.
Large files should be read line-by-line or in chunks, not slurped with read() into memory. For random access, binary mode plus seek/tell or memory-mapped files may apply.
Temporary files: tempfile module creates secure temp names; clean up in finally or use context managers. Atomic writes often mean write to a temp file then os.replace to the final path.
Cross-platform path logic belongs in pathlib, not manual string concatenation with \ or /.
Opening files without encoding="utf-8" on Windows text mode.
Forgetting to close files when not using with.
Mixing text and binary modes on the same handle.
Assuming current working directory instead of paths relative to project root or config.
Reading entire huge files into memory at once.
Use with open(...) or pathlib's Path.read_text / write_text for small files.
Specify UTF-8 explicitly for text; use binary mode for non-text data.
Prefer pathlib.Path over os.path string juggling.
Stream large inputs; combine with error handling for missing files.
Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.
The program below demonstrates open modes. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# Example: Open modes
# Run in the REPL or save as a .py file and execute with python.
with open("data.txt", "w") as f:
f.write("line one\n")
with open("data.txt", "a") as f:
f.write("line two\n")
with open("data.txt") as f:
print(f.read())
This sample walks through binary in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# Example: Binary
# Run in the REPL or save as a .py file and execute with python.
with open("logo.png", "rb") as f:
header = f.read(8)
print(header)
Here is a hands-on illustration of pathlib. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.
# Example: pathlib
# Run in the REPL or save as a .py file and execute with python.
from pathlib import Path
p = Path("data.txt")
print(p.exists(), p.stat().st_size, p.suffix)
The program below demonstrates path exists. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# pathlib.Path is the modern file system API
from pathlib import Path # OO paths
root = Path("data") # relative directory
root.mkdir(exist_ok=True) # create if missing
file = root / "notes.txt" # join with operator
file.write_text("hello\n", encoding="utf-8") # write unicode text
print(file.exists(), file.is_file()) # True True
print(file.read_text(encoding="utf-8")) # read back
print(file.stat().st_size) # byte size via stat
for child in root.iterdir(): # list directory
print(child.name) # file name
This sample walks through copy tree in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# shutil copies files and directories
import shutil # high-level file ops
from pathlib import Path # paths
src = Path("data/notes.txt") # existing file
dst = Path("backup/notes.txt") # destination path
dst.parent.mkdir(parents=True, exist_ok=True) # ensure parents
shutil.copy2(src, dst) # copy preserving metadata
print(dst.exists(), dst.read_text(encoding="utf-8")) # verify
shutil.disk_usage(".") # total/used/free for current drive
total, used, free = shutil.disk_usage(".") # unpack tuple
print(total // (1024**2), "MB total") # megabytes
Continue with these focused follow-up lessons on Python File Handling: