Courses/Python Mastery/Module 8: Errors & Files
Module 8 · Lesson 320 minBeginner

Reading and Writing Files

Lesson goal
Open, read, write, and close files the safe way (with).

Reading and Writing Files

Variables die with the program. Files are how data survives — and Python's with open(...) pattern makes reading and writing them one of the cleanest parts of the language.

Writing a file

code
with open("notes.txt", "w") as f:
    f.write("Line one\n")
    f.write("Line two\n")

print("saved!")

Decode the line:

  • `open("notes.txt", "w")` — open the file in mode "w" (write)
  • `as f` — the open file is called f inside the block
  • `f.write("...")` — write text (no automatic newline — hence the \ns)
  • `with` — the magic: when the block ends, the file is closed automatically, even if an error happened
  • Always use with. Manual open()/close() works but forgetfulness causes real data loss; with makes closing impossible to forget.

    The modes (there are only three you need)

    ModeNameBehavior
    "w"writeErases the file, then writes fresh
    "a"appendKeeps existing content, adds at the end
    "r"readRead only (the default)

    The "w" erasure surprises people:

    code
    with open("notes.txt", "w") as f:
        f.write("just this")
    
    # notes.txt now contains ONLY "just this" — previous content gone!

    Adding to a log or history? "a". Creating a fresh report? "w".

    Reading a file — three ways

    code
    with open("notes.txt", "r") as f:
        # Way 1: everything as one string
        content = f.read()
        print(content)
    
    with open("notes.txt") as f:          # "r" is the default
        # Way 2: list of lines (newlines included)
        lines = f.readlines()
        print(lines)                       # ['Line one\n', 'Line two\n']
    
    # Way 3: line by line — the memory-friendly way
    with open("notes.txt") as f:
        for line in f:
            print(line.strip())            # strip removes the trailing \n

    Way 3 is the professional default — it processes one line at a time, so even a million-line file never overloads memory. The .strip() in the loop matters: each line arrives with its invisible \n attached.

    The classic combo: write then read back

    code
    # Save
    with open("highscore.txt", "w") as f:
        f.write("1250")
    
    # Later... load
    with open("highscore.txt") as f:
        score = int(f.read().strip())
    
    print(f"High score: {score}")      # High score: 1250

    Notice int() — files give back strings, always. Numbers round-trip through str() and int().

    FileNotFoundError — your first handled file error

    code
    try:
        with open("ghost.txt") as f:
            print(f.read())
    except FileNotFoundError:
        print("That file doesn't exist!")

    Reading a missing file raises FileNotFoundError — wrap it when the file might legitimately be absent (first run of an app, optional config).

    Common Errors & Fixes

  • `FileNotFoundError` — wrong filename or wrong folder. Files open relative to *where you ran the script*, not where the script lives. Use full paths or run from the right directory.
  • File is empty after writing — you forgot f.write() entirely, or wrote after the with block closed.
  • `io.UnsupportedOperation: not readable` — opened with "w" (write-only) and tried to read. Reopen with "r" or "w+".
  • Weird `\n` at the end of every line — that's the line's real newline; .strip() it.

  • ✅ Checkpoint

  • Which mode erases the file first? *("w")*
  • Why with instead of open/close? *(Auto-closes even on errors — can't forget)*
  • What does each line from a for-loop read arrive with? *(A trailing \n — strip it)*
  • What type is everything read from a file? *(String — convert numbers yourself)*
  • Next: JSON and CSV — the two file formats that power real applications.