Files and errorsChapter 81 of 114
Write Files
Create, overwrite and append, without losing what was there.
write
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("first line\n")
f.write("second line\n")
with open("notes.txt", encoding="utf-8") as f:
print(f.read())Output
first line second line
write adds no newline of its own — unlike print — so you put \n where you want line breaks.
It returns the number of characters written, which is occasionally useful:
with open("notes.txt", "w", encoding="utf-8") as f:
print(f.write("hello"))Output
5
w empties the file first
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("original\n")
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("replacement\n")
with open("notes.txt", encoding="utf-8") as f:
print(f.read().strip())Output
replacement
The original is gone. That happens when the file is opened, not when you write, so opening in "w" and then failing still leaves you with an empty file.
a appends
with open("log.txt", "w", encoding="utf-8") as f:
f.write("one\n")
for line in ["two", "three"]:
with open("log.txt", "a", encoding="utf-8") as f:
f.write(line + "\n")
with open("log.txt", encoding="utf-8") as f:
print(f.read().strip())Output
one two three
Appending in a loop like that reopens the file each time. For anything hot, open once outside the loop.
writelines writes a list
lines = ["Ada\n", "Grace\n"]
with open("names.txt", "w", encoding="utf-8") as f:
f.writelines(lines)
with open("names.txt", encoding="utf-8") as f:
print(f.read().strip())Output
Ada Grace
with open("names.txt", "w", encoding="utf-8") as f:
f.writelines(["Ada", "Grace"])
with open("names.txt", encoding="utf-8") as f:
print(f.read())Output
AdaGrace
Building the text yourself is clearer:
names = ["Ada", "Grace"]
with open("names.txt", "w", encoding="utf-8") as f:
f.write("\n".join(names) + "\n")
with open("names.txt", encoding="utf-8") as f:
print(f.read().strip())Output
Ada Grace
print to a file
print takes a file argument, which brings its newline handling with it:
with open("report.txt", "w", encoding="utf-8") as f:
print("Title", file=f)
print("value:", 42, file=f)
with open("report.txt", encoding="utf-8") as f:
print(f.read().strip())Output
Title value: 42
pathlib for small files
from pathlib import Path
Path("short.txt").write_text("one line\n", encoding="utf-8")
print(Path("short.txt").read_text(encoding="utf-8").strip())Output
one line
It opens, writes and closes in one call. For anything you write incrementally, use with open.
Do not overwrite until you have succeeded
Writing straight over a file means a crash halfway leaves you with neither the old contents nor the new. Write to a temporary name and rename when it is safe:
import os
with open("data.txt", "w", encoding="utf-8") as f:
f.write("important\n")
with open("data.txt.tmp", "w", encoding="utf-8") as f:
f.write("new contents\n")
os.replace("data.txt.tmp", "data.txt")
with open("data.txt", encoding="utf-8") as f:
print(f.read().strip())Output
new contents
os.replace swaps the file in one step, so a reader sees either the old file or the new one and never a half-written mess.
Test yourself
2 questionsWhat does writelines() add between the strings?
Show the answer
Nothing — Despite the name it means 'write all these strings'. Build the text with join if you want separators.
Why write to a temporary name and then os.replace?
Show the answer
A crash halfway cannot leave you with a half-written file — The rename is a single step, so a reader sees either the old file or the new one.
Delete Files
Remove files and folders, and check first without a race.