Most real programs need to read data from files or save data to them — config files, logs, exported reports, user data. Python's built-in open() function handles all of this, and once you know the handful of modes it supports, file handling becomes one of the more straightforward parts of the language.
Opening Files: Read, Write, and Append Modes
open() takes a filename and a mode string. The most common modes are "r" (read — the default, fails if the file doesn't exist), "w" (write — creates the file if missing, and overwrites it completely if it already exists), and "a" (append — adds to the end of the file without erasing existing content).
# Writing creates the file and overwrites any existing content
with open("notes.txt", "w") as f:
f.write("First line\n")
f.write("Second line\n")
# Appending adds to the end without erasing what's there
with open("notes.txt", "a") as f:
f.write("Third line (appended)\n")
Reading a File: Whole File vs Line by Line
You can read everything at once with .read(), or loop over the file object directly to process it one line at a time — far more memory-efficient for large files, since it doesn't load the whole thing into memory first.
with open("notes.txt", "r") as f:
content = f.read()
print(content)
print("--- reading line by line ---")
with open("notes.txt", "r") as f:
for line in f:
print(line.strip()) # strip() removes the trailing newline
Output:
First line
Second line
Third line (appended)
--- reading line by line ---
First line
Second line
Third line (appended)
Why with open(...) Instead of open() Directly
You'll almost always see files opened with a with statement rather than a bare f = open(...). The with block guarantees the file is automatically closed as soon as the block ends — even if an error happens inside it — so you never accidentally leave a file handle open. This is the same pattern used by Python's broader context manager feature, which with relies on.
Common Pitfall: FileNotFoundError
Opening a file in "r" mode that doesn't exist raises a FileNotFoundError. Wrap file-reading code in a try/except block if the file's existence isn't guaranteed — for example, when a config file is optional and should fall back to defaults if it's missing.
