PythonFile Handling

File Handling

Almost every real program eventually needs to read data from disk or save results to a file — configuration files, logs, CSV exports, saved user data, and more. Python's built-in open() function is the entry point for all of this. This page covers how to open files safely, the different modes you can open them in, and the core methods for reading and writing.

Opening a File with `open()`

open() takes a file path and a mode string that tells Python what you intend to do with the file — read it, write to it, append to it, and so on. It returns a file object that you use to interact with the file's contents.

Python
f = open("notes.txt", "r")
contents = f.read()
print(contents)
f.close()

The example above works, but it has a problem: if an error happens between open() and f.close(), the file may never get closed. The next section explains why that matters and shows the safer, idiomatic way to do this.

File Modes

The second argument to open() is the mode. It controls whether the file is opened for reading or writing, whether existing content is kept or erased, and whether the data is treated as text or raw bytes.

Mode

Meaning

'r'

Read (default). Fails with FileNotFoundError if the file doesn't exist.

'w'

Write. Creates the file if missing, overwrites it completely if it exists.

'a'

Append. Creates the file if missing, adds new content to the end if it exists.

'x'

Exclusive create. Fails with FileExistsError if the file already exists.

'r+'

Read and write. File must already exist; does not truncate it.

'w+'

Read and write. Creates or overwrites the file first.

'rb'

Read in binary mode (returns bytes instead of str).

'wb'

Write in binary mode (overwrites, writes bytes).

'ab'

Append in binary mode.

Note
Add `b` to any mode for binary I/O (e.g. `'rb'`, `'wb'`) when working with non-text files like images, PDFs, or ZIP archives. Without `b`, Python decodes the bytes as text using a character encoding.
Always Use `with open(...) as f:`

Python's with statement wraps file access in a context manager that automatically calls f.close() for you — even if an exception is raised inside the block. This is the idiomatic way to work with files, and you should use it by default.

Python
with open("notes.txt", "r") as f:
    contents = f.read()
    print(contents)

# f.close() is called automatically here, even if read() had raised an error
Warning
Forgetting to close a file is a real bug, not just a style nitpick. An unclosed file keeps a resource open at the operating-system level, and for writes, data may sit in an in-memory buffer and never actually reach the disk until the file is closed or flushed. That means a crash before `close()` can silently lose written data.

Compare the risky manual version to the safe with version:

Python
# Risky: if write_report() raises, close() never runs
f = open("report.txt", "w")
write_report(f)
f.close()

# Safe: close() always runs, even on error
with open("report.txt", "w") as f:
    write_report(f)
Reading a File

Once a file is open for reading, there are three common ways to pull data out of it.

  • .read() — reads the entire file as one string.

  • .readline() — reads a single line at a time, including the trailing newline.

  • .readlines() — reads the whole file and returns a list of lines.

Python
with open("poem.txt", "r", encoding="utf-8") as f:
    whole_thing = f.read()
print(repr(whole_thing))

with open("poem.txt", "r", encoding="utf-8") as f:
    first_line = f.readline()
    second_line = f.readline()
print(repr(first_line))
print(repr(second_line))

with open("poem.txt", "r", encoding="utf-8") as f:
    all_lines = f.readlines()
print(all_lines)
'Roses are red\nViolets are blue\n'
'Roses are red\n'
'Violets are blue\n'
['Roses are red\n', 'Violets are blue\n']
Tip
`.readlines()` is convenient for small files, but it loads every line into memory as a list before you can use any of it. For large files, looping over the file object directly with `for line in f:` is more memory-efficient — see the Reading & Writing Files page for details.
Writing to a File

.write() writes a single string to the file. Unlike print(), it does not add a newline automatically — you have to include \\n yourself. .writelines() writes an iterable of strings in sequence, also without adding newlines between them.

Python
with open("todo.txt", "w", encoding="utf-8") as f:
    f.write("Buy milk\n")
    f.write("Walk the dog\n")

with open("todo.txt", "a", encoding="utf-8") as f:
    f.writelines(["Water the plants\n", "Read a book\n"])

with open("todo.txt", "r", encoding="utf-8") as f:
    print(f.read())
Buy milk
Walk the dog
Water the plants
Read a book
Checking Whether a File Exists

Opening a missing file in read mode raises FileNotFoundError. You can guard against this either by checking beforehand with os.path.exists(), or — usually the more Pythonic approach — by simply trying to open the file and catching the exception.

Python
import os

path = "settings.json"

if os.path.exists(path):
    with open(path, "r", encoding="utf-8") as f:
        print(f.read())
else:
    print(f"{path} does not exist yet")

# Alternative: try/except is often preferred (see also the pathlib page for .exists())
try:
    with open(path, "r", encoding="utf-8") as f:
        data = f.read()
except FileNotFoundError:
    print(f"{path} does not exist yet")
    data = ""
settings.json does not exist yet
settings.json does not exist yet
Note
The try/except approach avoids a subtle race condition: a file could theoretically be deleted between the `os.path.exists()` check and the `open()` call. Catching `FileNotFoundError` is generally considered more robust for this reason.
  • with open(...) as f: closes the file automatically and should be your default.

  • 'r', 'w', 'a', and 'x' control read/overwrite/append/create-only behavior.

  • .read(), .readline(), and .readlines() give you three levels of granularity for reading.

  • .write() and .writelines() do not add newlines automatically — you must include \n.