Reading and writing files lets your programs save data permanently, instead of losing everything the moment they stop running. This guide covers open(), file modes, and the safer with statement.
If you’ve worked through our guides on lists and tuples and functions, file handling is a natural next step — the point where your programs start interacting with persistent data.
Opening a File
Python’s built-in open() function is the starting point for all file operations. According to W3Schools’ guide to file handling, open() takes a filename and a mode, with four core modes: "r" for reading, "w" for writing (overwrites existing content), "a" for appending, and "x" for creating a new file (fails if it already exists).
file = open("notes.txt", "r")
content = file.read()
print(content)
file.close()The Safer Way: with Statement
Manually calling close() is easy to forget, especially if an error happens before that line runs. The with statement handles this automatically:
with open("notes.txt", "r") as file:
content = file.read()
print(content)
# file is automatically closed here, even if an error occurredAccording to freeCodeCamp’s guide to writing files in Python, using with is the recommended standard for file handling, since it guarantees the file gets closed properly no matter what happens inside the block.
Reading a File Three Ways
- read() — returns the entire file as one string.
- readline() — returns a single line each time it’s called.
- readlines() — returns a list, one string per line.
For most beginner use cases, looping directly over the file object — for line in file: — is the simplest and most memory-efficient option, since it processes one line at a time rather than loading everything into memory at once.
Writing to a File
with open("notes.txt", "w") as file:
file.write("First line.\n")
file.write("Second line.\n")Note the "w" mode overwrites the file entirely if it already exists — if you want to add content without erasing what’s there, use "a" (append) instead.
Handling Errors Gracefully
Trying to open a file that doesn’t exist raises a FileNotFoundError. Wrapping file operations in a try/except block prevents this from crashing your entire program:
try:
with open("missing.txt", "r") as file:
print(file.read())
except FileNotFoundError:
print("That file doesn't exist.")Working With File Paths
The filename you pass to open() can be relative (just the name, assuming the file sits in the same folder as your script) or absolute (a full path from the root of your filesystem). According to Programiz’s guide to Python file operations, using open("file1.txt") is equivalent to open("file1.txt", "r"), since read mode and text mode are both defaults you don’t need to specify explicitly.
A Practical Example: Logging Program Output
A genuinely common beginner use case is saving a program’s results to a file instead of just printing them:
scores = [85, 92, 78, 95]
with open("results.txt", "w") as file:
for score in scores:
file.write(f"Score: {score}\n")
file.write(f"Average: {sum(scores) / len(scores)}\n")This combines a loop, an f-string, and file writing into one small, genuinely useful script — the kind of thing that shows up constantly once you start building programs that need to save their output rather than just displaying it once and losing it.
Appending Instead of Overwriting
If you want to add new data to a file without erasing what’s already there, “a” mode is the one to use:
with open("log.txt", "a") as file:
file.write("New entry added.\n")Running this multiple times keeps adding new lines to the end of the file, rather than replacing its contents each time the way “w” mode would. This distinction trips up beginners occasionally — accidentally using “w” when you meant “a” is a common way to lose data you actually wanted to keep.
Reading Line by Line, Practically
Looping directly over a file object is both the simplest syntax and the most memory-efficient approach for larger files, since it reads one line at a time rather than loading the whole file into memory:
with open("notes.txt", "r") as file:
for line in file:
print(line.strip())The .strip() call removes the trailing newline character each line naturally includes, which otherwise produces an extra blank line when combined with print()‘s own automatic newline.
Common Questions
Do I always need to specify a mode? No — open("file.txt") defaults to read mode (“r”), so specifying it explicitly is optional but often clearer to read.
What happens if I write to a file that doesn’t exist? Both “w” and “a” modes create the file automatically if it isn’t there yet — only “r” mode requires the file to already exist.
Can I read and write to the same file at once? Yes, using modes like “r+”, though this is more advanced and easy to get wrong. For most beginner tasks, opening the file separately for reading and writing is simpler and safer.
What’s the difference between text mode and binary mode? Text mode (the default) treats file content as strings, handling line endings and encoding automatically. Binary mode (“rb”, “wb”) is for non-text files, like images, where you need the raw bytes exactly as stored, without any text-specific processing applied.
Working With CSV-Style Data
A common real-world file format is plain text where each line represents a record, with values separated by commas — a simplified version of what a CSV (comma-separated values) file looks like. You can parse this manually using string methods you already know:
with open("students.txt", "r") as file:
for line in file:
name, score = line.strip().split(",")
print(f"{name} scored {score}")Here, .split(",") breaks each line into a list based on the comma, and unpacking assigns the two resulting pieces directly to name and score. For genuinely serious CSV work, Python’s built-in csv module handles edge cases (like commas inside quoted values) more robustly than manual splitting — worth knowing exists once you’re working with real spreadsheet-style data.
Checking Whether a File Exists First
Sometimes you want to check whether a file exists before attempting to open it, rather than catching an exception after the fact. Python’s os.path.exists() function handles this directly:
import os
if os.path.exists("notes.txt"):
with open("notes.txt", "r") as file:
print(file.read())
else:
print("File not found.")Both this approach and the try/except pattern covered earlier are valid — the try/except version is generally preferred in Python, since checking first and then opening still leaves a small window where the file could theoretically be deleted between the check and the actual open call.
Text Encoding: A Brief Note
Files are stored as bytes, and encoding is the rule for translating those bytes into readable characters. UTF-8 is the modern standard and handles virtually every language and symbol correctly, but occasionally you’ll encounter a UnicodeDecodeError when opening a file saved with an older or different encoding. Specifying the encoding explicitly often resolves this:
with open("notes.txt", "r", encoding="utf-8") as file:
content = file.read()As a beginner, you won’t hit this often, but it’s worth recognizing the error message if it ever shows up, rather than assuming something is fundamentally broken with your code.
Deleting a File
File deletion isn’t handled by open() at all — it requires Python’s os module:
import os
if os.path.exists("old_notes.txt"):
os.remove("old_notes.txt")Checking existence first, as shown here, avoids an error if the file is already gone. Deletion is permanent — there’s no built-in recycle bin — so it’s worth double-checking the filename before running code that removes files, especially in scripts you’re still actively developing and testing.
Quick-Reference Guide
- open(filename, mode) — “r” read, “w” write (overwrites), “a” append, “x” create.
- with open(…) as file: — automatically closes the file, even on error.
- read() / readline() / readlines() — different ways to read content.
- write() — adds text to a file opened in write or append mode.
- FileNotFoundError — handle with try/except when a file might not exist.
Conclusion
File handling connects your Python programs to persistent data — the ability to save something today and read it back tomorrow, long after the program that created it has stopped running. The with statement, paired with the right mode, covers the vast majority of what a beginner needs for reading and writing text files reliably, without needing to manage the more complex edge cases manually.
The best way to build comfort here is direct practice: write a small script that saves a few lines of text to a file, then write a second script that reads that same file back and prints its contents, confirming the round trip actually works end to end.
In the next guide in this series, we’ll cover common Python errors and how to read and fix them, building directly on the exception handling introduced here.
Explore More Python for Beginners Guides →

When not writing, Alex is probably debugging someone else’s code.