File I/O introduction

Reading and writing files

Everything your programs have worked with so far has lived in memory — variables, lists, dicts — and vanished the moment the program ended. Files let a program persist data, read data other programs (or humans) produced, and exchange data with the outside world.

Opening a file

f = open("notes.txt")
content = f.read()
f.close()

This works, but it has a real problem: if something goes wrong between open and close (an exception, a return you forgot moves past the close, anything), the file is left open — a resource leak. On a short-lived script that rarely matters, but it's a bad habit to build.

The with statement

Python's fix is the with statement — it guarantees the file gets closed automatically, no matter how the block exits (normally, via return, or via an exception):

with open("notes.txt") as f:
    content = f.read()
# f is automatically closed here, even if read() raised

This is the idiomatic way to work with files in Python, and the only style you'll use in this section. open(path) as f gives you a file object f inside the block; once the block ends (for any reason), the file is closed for you.

Modes

open() takes a second argument controlling how the file is opened:

open(path)          # "r" (read) is the default -- fails if the file doesn't exist
open(path, "r")      # read -- same as above, explicit
open(path, "w")      # write -- creates the file if it doesn't exist, ERASES existing content
open(path, "a")      # append -- creates the file if it doesn't exist, adds to the end otherwise

The difference between "w" and "a" matters a lot: opening a file in "w" mode truncates it — whatever was there before is gone the moment you open it, even if you never write anything. If you want to add to a file without destroying what's already in it, you need "a".