6 Essential Facts About File Handling in Python

File Handling in Python

File handling in Python is one of the most practical skills you’ll use in real programs, letting you read from and write to files on disk.

Almost every real program eventually needs to read data from a file, or save data to one, whether that’s a configuration file, a log, or a dataset. Python handles this through a built-in open() function that hands back a file object, which you can then read from or write to before closing it again.

Opening and Reading a File

file = open('notes.txt', 'r')
contents = file.read()
print(contents)
file.close()

open() takes a filename and a mode that says what you intend to do with the file. 'r', the default, opens it for reading only, and fails with an error if the file doesn’t already exist. Once you’re done with a file, calling close() releases it back to the operating system – and this matters more than it looks like it should.

Why close() Is Easy to Forget – and Why That’s a Problem

Every open file consumes a resource the operating system is tracking on your program’s behalf, and every operating system caps how many a single process can have open at once. Forgetting to call close() doesn’t usually cause a problem in a short script that opens one file and exits, but in a long-running program that opens many files over time – a web server handling one request per visitor, say – each unclosed file quietly eats into that limit. Eventually the process hits the cap and every further attempt to open a file starts failing with an operating-system error, and by then the actual bug, a missing close() call somewhere far upstream, can be genuinely difficult to track back down. Worse, if an error happens between open() and close(), the code jumps straight past the close() call entirely, leaking the file handle every time that error path is hit.

The with Statement: Files That Close Themselves

Python’s standard solution is the with statement, which guarantees a file gets closed the moment its block ends – whether that block finishes normally or exits early because of an error.

with open('notes.txt', 'r') as file:
    contents = file.read()
    print(contents)

# file is already closed here, automatically

This works because file objects implement what Python calls the context manager protocol: two special methods, __enter__() and __exit__(). When a with block starts, Python calls __enter__(), which here opens the file and hands it back as the value bound to file. When the block ends – for any reason at all, including an exception raised partway through – Python calls __exit__(), which closes the file. The practical effect is that with removes an entire category of resource-leak bugs simply by making it structurally impossible to forget the cleanup step, which is why it’s considered the standard, idiomatic way to work with files in Python rather than calling open() and close() by hand.

Reading Line by Line

Reading an entire file into memory at once with read() works fine for small files, but a file object is also directly iterable, which lets you loop over it one line at a time – useful for large files, since Python never has to hold more than one line in memory at once.

with open('notes.txt', 'r') as file:
    for line in file:
        print(line.strip())

Each line comes back with its trailing newline character still attached, which is why strip() is used above to remove it before printing. Two other common alternatives are readline(), which reads a single line each time you call it, and readlines(), which reads the whole file at once and returns it as a list of lines.

Writing to a File

Switching the mode from 'r' to 'w' opens a file for writing instead of reading – but with an important catch: 'w' mode erases the file’s existing content the instant it’s opened, even if you never actually write anything. If a file with that name doesn’t exist yet, 'w' mode creates it.

with open('notes.txt', 'w') as file:
    file.write('First line\n')
    file.write('Second line\n')

Unlike print(), write() does not add a newline character automatically, so each line here spells one out explicitly with \n. If you want to keep a file’s existing content and add new content after it rather than replacing everything, use append mode, 'a', instead: it also creates the file if it doesn’t exist, but positions new writes after whatever is already there rather than erasing it first.

with open('notes.txt', 'a') as file:
    file.write('Third line, added later\n')

A Quick Reference to File Modes

  • 'r' – read only (default); the file must already exist
  • 'w' – write only; creates the file if missing, erases existing content immediately on open
  • 'a' – append; creates the file if missing, writes are added after existing content
  • 'r+' – read and write; the file must already exist, and nothing is erased on open
  • 'w+' – read and write; behaves like 'w', so existing content is erased on open

Adding 'b' to any of these – 'rb', 'wb', and so on – opens the file in binary mode, for content that isn’t plain text, such as images or other non-text files, where the raw bytes need to be handled exactly as they are rather than decoded as text.

Handling a Missing File Gracefully

Trying to open a file that doesn’t exist in read mode raises a FileNotFoundError. Wrapping the attempt in a try/except block lets your program respond to that situation instead of crashing outright.

try:
    with open('does_not_exist.txt', 'r') as file:
        print(file.read())
except FileNotFoundError:
    print('That file does not exist yet.')

with and try/except combine naturally here: the with block still guarantees proper cleanup of any file that does get opened successfully, while the surrounding except catches the specific case where opening it failed in the first place.

No Comments

Leave a Reply

Recent Comments

No comments to show.