Python with open() as file: Read, Write and Append Safely

with open("file.txt") as file: is how Python opens a file safely. The with block gives you the file object as file, and Python closes it automatically when the block ends, even if an error happens inside:

with open("stores.txt", encoding="utf-8") as file:
    text = file.read()
# file is closed here

You’ll see real output from Python 3.12.5 on Windows 11 in every Command Prompt screenshot below.

I’ll cover reading and writing, the file modes, what happens on errors, the encoding trap on Windows, opening two files at once, and writing your own context manager.

How with open as file works in Python

open() returns a file object, as file names it, and the indented block uses it. Check file.closed and you can see exactly when Python closes it:

with open("stores.txt", "w", encoding="utf-8") as file:
    file.write("Austin, TX\n")
    file.write("Denver, CO\n")
    file.write("Boston, MA\n")

with open("stores.txt", encoding="utf-8") as file:
    text = file.read()
    print("inside with, closed?", file.closed)

print(text, end="")
print("after with, closed? ", file.closed)

Output:

inside with, closed? False
Austin, TX
Denver, CO
Boston, MA
after with, closed?  True
Command Prompt output of Python with open as file writing three US cities, reading them back, and showing the file closed after the block
Inside the block closed is False; right after it, True. You never call close() yourself.

The variable still exists after the block, so text is usable. The file itself is closed, though, so reading from it again would raise a ValueError.

Reading a file line by line with with open

Looping over the file object reads one line at a time, which keeps memory low on big files. Use read().splitlines() when you want the whole thing as a list:

with open("stores.txt", "w", encoding="utf-8") as f:
    f.write("Austin, TX\nDenver, CO\nBoston, MA\n")

with open("stores.txt", encoding="utf-8") as f:
    for n, line in enumerate(f, start=1):      # one line at a time, low memory
        city, state = line.strip().split(", ")
        print(n, city, "->", state)

with open("stores.txt", encoding="utf-8") as f:
    print(f.read().splitlines())                 # whole file as a list, no newlines

Output:

1 Austin -> TX
2 Denver -> CO
3 Boston -> MA
['Austin, TX', 'Denver, CO', 'Boston, MA']

Each line keeps its \n, so strip() it before splitting. There’s more in reading a file line by line in Python.

File modes: r, w, a and x

The second argument to open() sets the mode. Be careful with "w": it empties the file the moment it opens it:

with open("log.txt", "w", encoding="utf-8") as f:     # w: create or wipe
    f.write("09:00 store opened\n")

with open("log.txt", "a", encoding="utf-8") as f:     # a: add to the end
    f.write("17:30 store closed\n")

with open("log.txt", encoding="utf-8") as f:          # r is the default
    print(f.read(), end="")

try:
    with open("log.txt", "x", encoding="utf-8") as f: # x: only if it doesn't exist
        f.write("never written")
except FileExistsError as err:
    print("FileExistsError:", err)

Output:

09:00 store opened
17:30 store closed
FileExistsError: [Errno 17] File exists: 'log.txt'
Command Prompt output of Python with open in write, append and read modes, and a FileExistsError from exclusive create mode x
a added the second line, and x refused to touch a file that already exists.
ModeMeaningIf the file existsIf it doesn’t
"r"Read (default)Opens itFileNotFoundError
"w"WriteWipes itCreates it
"a"AppendWrites at the endCreates it
"x"Exclusive createFileExistsErrorCreates it
"rb", "wb"Binary read or writeBytes, no encodingSame as r / w
"r+"Read and writeOpens itFileNotFoundError

What happens if an error occurs inside with open?

The file still gets closed. That’s the whole point of with: cleanup runs whether the block finishes normally or raises. A missing file fails earlier, at open():

try:
    with open("orders_2026.csv", encoding="utf-8") as f:
        print(f.read())
except FileNotFoundError as err:
    print("FileNotFoundError:", err)

try:
    with open("receipt.txt", "w", encoding="utf-8") as f:
        f.write("Total: $42.50\n")
        raise ValueError("card declined")       # something goes wrong mid-way
except ValueError as err:
    print("ValueError:", err, "| file closed anyway?", f.closed)

Output:

FileNotFoundError: [Errno 2] No such file or directory: 'orders_2026.csv'
ValueError: card declined | file closed anyway? True

Wrap the with in try when a missing file is a normal case, like an optional config. See checking if a file exists in Python for other approaches.

Always pass encoding=”utf-8″ to open() on Windows

Without encoding=, open() uses your system’s default. On a US Windows PC that’s usually cp1252, so UTF-8 text like “Café” or an en dash comes back garbled:

import locale

with open("menu.txt", "w", encoding="utf-8") as f:
    f.write("Café latte – $4.75\n")

print("Windows default encoding:", locale.getencoding())

with open("menu.txt", encoding="cp1252") as f:    # what you get without encoding= here
    print("wrong:", f.read(), end="")

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

Output:

Windows default encoding: cp1252
wrong: Café latte – $4.75
right: Café latte – $4.75
Command Prompt output of a UTF-8 file read with the cp1252 Windows default encoding showing garbled characters, then read correctly with utf-8
Same file, two encodings. Only encoding="utf-8" reads it back correctly.

I add encoding="utf-8" to every text-mode open() call. It makes your script behave the same on Windows, macOS and Linux.

Opening two files in one with statement

Separate the files with commas. Since Python 3.10 you can wrap them in parentheses and put each on its own line, which reads much better:

with open("prices.txt", "w", encoding="utf-8") as f:
    f.write("19.99\n5.49\n102.00\n")

with (
    open("prices.txt", encoding="utf-8") as src,
    open("prices_with_tax.txt", "w", encoding="utf-8") as dst,
):
    for line in src:
        dst.write(f"{float(line) * 1.08:.2f}\n")

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

Output:

['21.59', '5.93', '110.16']
Command Prompt output of a Python with statement opening a source and destination file together to add 8 percent tax to prices
Both files close when the block ends, in reverse order.

Writing your own context manager

open() isn’t special. Any object with __enter__ and __exit__ works with with, and contextlib.contextmanager turns a generator into one:

from contextlib import contextmanager
import time

@contextmanager
def timer(label):
    start = time.perf_counter()
    try:
        yield
    finally:
        print(f"{label} took {time.perf_counter() - start:.3f} s")

with timer("writing 10,000 lines"):
    with open("big.txt", "w", encoding="utf-8") as f:
        for i in range(10_000):
            f.write(f"order {i}\n")

with open("big.txt", encoding="utf-8") as f:
    print(sum(1 for _ in f), "lines written")

Output:

writing 10,000 lines took 0.004 s
10000 lines written

The code before yield runs on entry, and the finally part runs on exit. Locks, database connections and timers all use this pattern. For writing other data types, see writing a list to a file.

Here are more file guides you can try next:

Frequently asked questions

What does with open as file mean in Python?

It opens a file, names the file object (here file), and closes it automatically when the indented block ends.

Do I need to call close() with a with statement?

No. The with statement closes the file for you, even if an exception is raised inside the block.

How do I append to a file instead of overwriting it?

Open it in append mode: with open("log.txt", "a", encoding="utf-8") as f:.

Can I open multiple files with one with statement?

Yes. Separate them with commas, or wrap them in parentheses on separate lines in Python 3.10 and newer.

Why does my file show strange characters?

It was read with the wrong encoding. Pass encoding="utf-8" to open().

Where are all the open() options documented?

Modes, buffering, encoding and newline handling are all in the open() function docs.