Chapters
Python114 chapters

Files and errorsChapter 79 of 114

File Handling

Open a file safely, in the right mode, with the right encoding.

Always use with

open() gives you a file object. with closes it for you, even if something goes wrong in the middle:

Python
with open("notes.txt", "w") as f:
    f.write("first line\n")

with open("notes.txt") as f:
    print(f.read())

Output

first line

Without with you have to remember f.close(), and an exception before it leaves the file open. There is no reason to write that version.

The modes

ModeDoesIf the file exists
"r"read (the default)reads it
"w"writeempties it first
"a"appendadds at the end
"x"createraises FileExistsError
"r+"read and writekeeps the contents
Python
with open("log.txt", "w") as f:
    f.write("one\n")

with open("log.txt", "a") as f:
    f.write("two\n")

with open("log.txt") as f:
    print(f.read())

Output

one
two

"x" is the safe way to say "only if it is not already there":

Python
with open("once.txt", "x") as f:
    f.write("created")

try:
    with open("once.txt", "x") as f:
        f.write("again")
except FileExistsError:
    print("already exists, refused to overwrite")

Output

already exists, refused to overwrite

Text and binary

By default a file is text: you read and write str, and Python decodes the bytes for you. Add "b" for raw bytes:

Python
with open("data.bin", "wb") as f:
    f.write(b"\x00\x01\x02")

with open("data.bin", "rb") as f:
    print(f.read())

Output

b'\x00\x01\x02'

Use binary mode for images, archives and anything that is not text.

Always name the encoding

Python
with open("city.txt", "w", encoding="utf-8") as f:
    f.write("München")

with open("city.txt", encoding="utf-8") as f:
    print(f.read())

Output

München

Without encoding=, Python uses the platform default, which is UTF-8 on Linux and macOS and historically something else on Windows. The same code then produces different results on different machines. Passing encoding="utf-8" costs nothing and removes a whole category of bug.

Missing files

Python
try:
    with open("not-there.txt") as f:
        print(f.read())
except FileNotFoundError:
    print("no such file")

Output

no such file

Catching the error beats checking first: between a check and the open, the file could disappear.

pathlib

For paths, pathlib is clearer than string joining and works on every platform:

Python
from pathlib import Path

p = Path("notes.txt")
p.write_text("written with pathlib\n", encoding="utf-8")

print(p.read_text(encoding="utf-8").strip())
print(p.name, p.suffix, p.stem)
print(p.exists())

Output

written with pathlib
notes.txt .txt notes
True

Path("folder") / "file.txt" builds a path with the right separator, so you never have to think about backslashes.

Python
from pathlib import Path

path = Path("data") / "reports" / "march.csv"
print(path.parts)
print(path.name, path.parent.name)

Output

('data', 'reports', 'march.csv')
march.csv reports

Printing the path itself would show forward slashes here and backslashes on Windows, which is the whole point: you write it once and pathlib renders it for whichever machine it runs on.

Test yourself

2 questions

What does opening a file in "w" mode do to an existing file?

Show the answer

Empties it immediately, before you write anything — Use "a" to append and "x" to refuse if the file already exists.

Why pass encoding="utf-8" explicitly?

Show the answer

The default depends on the platform, so the same code behaves differently — It costs nothing and removes a whole category of works-on-my-machine bug.

Next chapter

Read Files

Read a whole file, a line at a time, or lazily for a big one.