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.