Python Basics — Context Managers

2 minute read

Context Managers

The with statement is the proper way to work with files in Python. It automatically handles cleanup — closing files, releasing resources — even if an error occurs.

The Problem Without with

# ❌ Manual approach — risky
f = open("data.txt", "r")
content = f.read()
# What if an error happens here? File stays open!
f.close()

# ❌ Slightly better but verbose
f = open("data.txt", "r")
try:
    content = f.read()
finally:
    f.close()

The with Statement (The Proper Way)

# ✅ Clean, safe, automatic
with open("data.txt", "r") as f:
    content = f.read()
# File is automatically closed here — even if an error occurred!

What happens:

  1. open() returns a file object
  2. The file is assigned to f
  3. The code block runs
  4. When the block exits (normally or due to an error), f.close() is called automatically

Why Context Managers Matter

# Without context manager — file might stay open
def read_unsafe():
    f = open("big_file.txt")
    data = f.read()
    # If an exception occurs here, f is never closed
    process(data)  # potential error
    f.close()      # never reached if process() fails

# With context manager — always closed
def read_safe():
    with open("big_file.txt") as f:
        data = f.read()
        process(data)  # even if this fails, f is closed

Multiple Files in One with Block

# Python 3.1+
with open("input.txt", "r") as infile, open("output.txt", "w") as outfile:
    for line in infile:
        outfile.write(line.upper())

# Python 3.10+ — parenthesized context managers
with (
    open("input.txt", "r") as infile,
    open("output.txt", "w") as outfile,
):
    for line in infile:
        outfile.write(line.upper())

Practical Example: File Copy

with open("source.txt", "r") as src, open("dest.txt", "w") as dst:
    for line in src:
        dst.write(line)
print("File copied!")

Verifying the File Is Closed

with open("sample.txt", "r") as f:
    print(f"Inside: closed = {f.closed}")   # False
    content = f.read()

print(f"Outside: closed = {f.closed}")      # True

contextlib Basics

The contextlib module provides utilities for creating context managers:

contextlib.suppress — Ignore Specific Exceptions

from contextlib import suppress

# Instead of:
try:
    os.remove("temp.txt")
except FileNotFoundError:
    pass

# Use:
import os
from contextlib import suppress

with suppress(FileNotFoundError):
    os.remove("temp.txt")

contextlib.redirect_stdout — Capture Print Output

from contextlib import redirect_stdout
import io

output = io.StringIO()
with redirect_stdout(output):
    print("This goes to the variable, not the screen")

captured = output.getvalue()
print(f"Captured: {captured.strip()}")
# Captured: This goes to the variable, not the screen

Context Managers Beyond Files

The with statement isn’t just for files — many Python objects support it:

# Database connections
import sqlite3

with sqlite3.connect("my_database.db") as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT 1")
# Connection auto-committed and closed

# Thread locks
import threading

lock = threading.Lock()
with lock:
    # Only one thread can be here at a time
    shared_resource += 1

Summary

  • Always use with when working with files — it’s the Python way
  • The with block guarantees close() is called, even on errors
  • Multiple files can be opened in a single with statement
  • contextlib provides extra tools: suppress, redirect_stdout, and more
  • Context managers work beyond files: databases, locks, network connections
  • Rule: If you see open() without with, it’s a code smell

Next up: CSV Files →

Updated: