Programs become useful when they outlive a single run. Files are the simplest way to make that happen.
Files here are real, but private
The playgrounds on this page write to a filesystem inside your browser tab. The code is exactly what you would run on your own machine, but nothing touches your actual disk, and everything vanishes when the runtime restarts.
Writing
with open("notes.txt", "w") as file:
file.write("First line\n")
file.write("Second line\n")
# Reading it straight back:
with open("notes.txt") as file:
print(file.read())Three things to notice:
withopens the file and closes it automatically, even if an error is raised inside the block. Always use it."w"means write, and truncates the file if it already exists.writedoes not add newlines. You supply them.
The modes
| Mode | Meaning |
|---|---|
"r" | Read (the default). Fails if the file is missing. |
"w" | Write. Creates or empties the file. |
"a" | Append. Creates if missing, writes to the end. |
"x" | Create. Fails if the file already exists. |
with open("log.txt", "w") as file:
file.write("start\n")
for step in ["load", "process", "save"]:
with open("log.txt", "a") as file:
file.write(f"{step}\n")
with open("log.txt") as file:
print(file.read())Reading, three ways
with open("poem.txt", "w") as file:
file.write("Roses are red\nViolets are blue\nPython is whitespace\n")
# 1. The whole thing as one string:
with open("poem.txt") as file:
print(repr(file.read()))
# 2. As a list of lines, newlines included:
with open("poem.txt") as file:
print(file.readlines())
# 3. Line by line - the memory-friendly way:
with open("poem.txt") as file:
for number, line in enumerate(file, start=1):
print(f"{number}: {line.rstrip()}")Option three is the one to default to. It never holds more than one line in
memory, so it works the same on a 3-line file and a 3-gigabyte one.
.rstrip() removes the trailing newline that each line carries.
Handling a missing file
def load_settings(path):
try:
with open(path) as file:
return file.read()
except FileNotFoundError:
print(f" {path} not found - using defaults")
return "theme=light"
print(load_settings("settings.txt"))Working with tabular data
CSV is the format the world exchanges tables in. Python’s csv module handles
the awkward parts — quoted fields, embedded commas — that a naive split(",")
gets wrong.
import csv
with open("sales.csv", "w") as file:
file.write("product,units,price\n")
file.write("keyboard,3,49.99\n")
file.write('"widget, large",2,15.00\n')
with open("sales.csv") as file:
reader = csv.DictReader(file)
total = 0
for row in reader:
line_total = int(row["units"]) * float(row["price"])
total += line_total
print(f"{row['product']:15} {line_total:8.2f}")
print(f"{'TOTAL':15} {total:8.2f}")DictReader uses the header row as keys, so row["price"] beats row[2] for
readability — and survives someone reordering the columns.
Everything read from a file is text
row["units"] is "3", not 3. The same conversion rule as input() on Day 1
applies here, for the same reason.
Writing a CSV
import csv
rows = [
{"name": "Ada", "score": 95},
{"name": "Alan", "score": 88},
]
with open("scores.csv", "w", newline="") as file:
writer = csv.DictWriter(file, fieldnames=["name", "score"])
writer.writeheader()
writer.writerows(rows)
with open("scores.csv") as file:
print(file.read())newline="" is the one incantation to remember — without it, Windows produces
a blank line between every row.
Word frequency from a file
Write the paragraph to a file, then read it back and print the five most common
words with their counts. Lower-case everything and strip punctuation with
strip(".,!?").
text = """the sun rose and the birds sang
the day was bright and the air was clear
the sun warmed the stones"""
with open("morning.txt", "w") as file:
file.write(text)
# Your code hereShow one solution
text = """the sun rose and the birds sang
the day was bright and the air was clear
the sun warmed the stones"""
with open("morning.txt", "w") as file:
file.write(text)
counts = {}
with open("morning.txt") as file:
for line in file:
for word in line.lower().split():
word = word.strip(".,!?")
counts[word] = counts.get(word, 0) + 1
ranked = sorted(counts.items(), key=lambda pair: (-pair[1], pair[0]))
for word, count in ranked[:5]:
print(f"{word:8} {count}")Reading line by line and counting as you go means this same code would work unchanged on a file too large to fit in memory.
What you learned
with open(path, mode) as file:opens and reliably closes a file."r"reads,"w"truncates,"a"appends,"x"refuses to overwrite.- Iterating a file yields lines, one at a time, with newlines attached.
FileNotFoundErroris the one to catch around reads.- Use the
csvmodule for tabular data; everything read is text.