Programs that remember
Everything you have computed so far vanished the moment the program ended, because variables live only in memory. Real software must persist data: save the game, keep a log, read yesterday's records back in. The operating system's unit of durable storage is the file, a named sequence of bytes on disk, and Python's built-in open() is the door to it.
f = open("todo.txt", "w") # open for writing f.write("buy milk\n") f.close()
open(path, mode) returns a file object, your handle on the open file. The mode string declares your intent:
| Mode | Meaning |
|---|---|
"r" | read (the default) |
"w" | write, erasing whatever the file held |
"a" | append to the end |
write puts text into the file exactly as given, so you add the \n line breaks yourself. close() tells the operating system you are done, which flushes any buffered text to disk and frees the handle. Forgetting it risks losing the last writes.
Writing a file, then reading it back
The first half creates the file and writes two lines, the second half reopens it for reading and prints each line.
f = open("todo.txt", "w") f.write("buy milk\n") f.write("call ada\n") f.close() f = open("todo.txt") for line in f: print(line.strip()) f.close()
Output
buy milk call ada
Two details carry most of the weight here. Every write call has to supply its own \n, because write adds nothing for you, unlike print. And every line read back includes that newline character, which is why line.strip() appears before printing. Without it each line would arrive with its newline and print would add another, doubling the blank space.
The close() calls matter too. Until a file is closed, written data may still be sitting in a buffer rather than on disk. The next lesson replaces this manual bookkeeping with a with block that cannot forget.
Three ways to read
f.read()returns the whole file as one string, fine for small files.f.readlines()returns a list of the lines.for line in f:streams one line at a time, the professional default.
The loop works because a file object is an iterator over its lines, the lesson 4-1 protocol again. Two consequences follow from what you already know about iterators:
- Lines arrive one at a time, so a 10-gigabyte log file never has to fit in memory.
- The iterator is one-shot: a second loop over the same open file gets nothing, because the read position is already at the end.
Every line keeps its trailing \n, so strip() is the usual companion, and int(line) works directly because int ignores surrounding whitespace.
Round-tripping numbers through a file
Numbers have to become text on the way out and numbers again on the way back in. This code writes three numbers one per line, then reopens the file and totals them.
nums = [3, 9, 27] f = open("nums.txt", "w") for n in nums: f.write(str(n) + "\n") f.close() f = open("nums.txt") total = 0 for line in f: total += int(line) f.close() print(total)
Output
39write only accepts strings, so each number goes through str(n) and the newline is appended by hand. Passing 3 directly would raise a TypeError.
The file is opened twice, once with "w" to create and write it, and once with the default "r" mode to read it. On the way back, int(line) handles the trailing newline without complaint, because int ignores surrounding whitespace, so int("27\n") is simply 27.
The mode that destroys data
If notes.txt already holds 100 lines, open("notes.txt", "w") erases all of them immediately.
Mode "w" truncates. The old content is gone the moment open returns, even if you never write a single byte afterwards and even if the program crashes on the next line. Nothing warns you and nothing asks for confirmation.
| Mode | Opens for | If the file exists |
|---|---|---|
"r" | reading (the default) | left untouched |
"w" | writing | contents erased |
"a" | appending | writes go to the end |
"x" | writing a new file | raises FileExistsError |
An accidental
"w"is one of the classic ways to destroy data. When you mean to add to a file, the mode is"a", and it is worth reading that character twice before running anything.