Context Managers (with statement) in Python
Mastering Context Managers in Python: A Comprehensive Guide
Opening a file, connecting to a database, or acquiring a lock all share a pattern: you set something up, use it, and must clean it up afterwards — even if an error occurs. Context managers automate this setup-and-teardown through the withwith statement, guaranteeing cleanup happens. In this guide, we’ll explore the withwith statement, the __enter____enter__ and __exit____exit__ methods, and the contextlibcontextlib module.
The Problem Context Managers Solve
Without a context manager, you must remember to release resources manually — and handle errors so cleanup still runs.
# Manual cleanup - easy to forget, fragile if an error occurs
file = open("data.txt", "w")
try:
file.write("Hello")
finally:
file.close() # must remember this, even on error# Manual cleanup - easy to forget, fragile if an error occurs
file = open("data.txt", "w")
try:
file.write("Hello")
finally:
file.close() # must remember this, even on errorThe withwith statement does all of this for you.
The with Statement
The withwith statement wraps a block of code so that resources are automatically cleaned up when the block ends — whether it ends normally or because of an exception.
The syntax of the withwith statement in Python is as follows:
with expression as variable:
# code that uses the resourcewith expression as variable:
# code that uses the resourceThe classic example is file handling:
with open("data.txt", "w") as file:
file.write("Hello, World!")
# file is automatically closed here - even if write() raised an error
print("File closed:", file.closed)with open("data.txt", "w") as file:
file.write("Hello, World!")
# file is automatically closed here - even if write() raised an error
print("File closed:", file.closed)Output:
C:\Users\Your Name> python with_file.py
File closed: TrueC:\Users\Your Name> python with_file.py
File closed: TrueDiagram:
graph TD
A[Enter with block] --> B[Call __enter__: set up resource]
B --> C[Run the indented body]
C --> D{Exception?}
D -->|No| E[Call __exit__: clean up]
D -->|Yes| F[Call __exit__: clean up, then handle]
E --> G[Continue program]
F --> G[Continue program]
What is a Context Manager?
A context manager is any object that implements two special methods:
__enter__(self)__enter__(self)— runs when thewithwithblock is entered. Its return value is bound to theasasvariable.__exit__(self, exc_type, exc_value, traceback)__exit__(self, exc_type, exc_value, traceback)— runs when the block exits, for cleanup. It receives details of any exception that occurred.
Building a Class-Based Context Manager
Let’s build a TimerTimer that measures how long the withwith block takes.
import time
class Timer:
def __enter__(self):
self.start = time.time()
print("Timer started")
return self # bound to the 'as' variable
def __exit__(self, exc_type, exc_value, traceback):
elapsed = time.time() - self.start
print(f"Elapsed: {elapsed:.2f}s")
return False # do not suppress exceptions
with Timer() as t:
total = sum(range(1_000_000))
print("Done")import time
class Timer:
def __enter__(self):
self.start = time.time()
print("Timer started")
return self # bound to the 'as' variable
def __exit__(self, exc_type, exc_value, traceback):
elapsed = time.time() - self.start
print(f"Elapsed: {elapsed:.2f}s")
return False # do not suppress exceptions
with Timer() as t:
total = sum(range(1_000_000))
print("Done")Output:
C:\Users\Your Name> python timer.py
Timer started
Elapsed: 0.03s
DoneC:\Users\Your Name> python timer.py
Timer started
Elapsed: 0.03s
DoneThe exit Return Value
If __exit____exit__ returns TrueTrue, any exception raised inside the block is suppressed (swallowed). If it returns FalseFalse (or NoneNone), the exception propagates normally after cleanup. Most context managers return FalseFalse.
class Suppressor:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is ValueError:
print("Suppressed a ValueError")
return True # swallow ValueError
return False
with Suppressor():
raise ValueError("boom") # caught and suppressed
print("Program continues")class Suppressor:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is ValueError:
print("Suppressed a ValueError")
return True # swallow ValueError
return False
with Suppressor():
raise ValueError("boom") # caught and suppressed
print("Program continues")Output:
C:\Users\Your Name> python suppress.py
Suppressed a ValueError
Program continuesC:\Users\Your Name> python suppress.py
Suppressed a ValueError
Program continuesThe contextlib Approach
Writing a whole class can be heavy. The contextlib.contextmanagercontextlib.contextmanager decorator lets you build a context manager from a simple generator function. The code before yieldyield is the setup (__enter____enter__), and the code after yieldyield is the cleanup (__exit____exit__).
from contextlib import contextmanager
import time
@contextmanager
def timer():
start = time.time() # setup (like __enter__)
print("Timer started")
yield # the 'with' body runs here
elapsed = time.time() - start
print(f"Elapsed: {elapsed:.2f}s") # cleanup (like __exit__)
with timer():
total = sum(range(1_000_000))from contextlib import contextmanager
import time
@contextmanager
def timer():
start = time.time() # setup (like __enter__)
print("Timer started")
yield # the 'with' body runs here
elapsed = time.time() - start
print(f"Elapsed: {elapsed:.2f}s") # cleanup (like __exit__)
with timer():
total = sum(range(1_000_000))Output:
C:\Users\Your Name> python contextlib_timer.py
Timer started
Elapsed: 0.03sC:\Users\Your Name> python contextlib_timer.py
Timer started
Elapsed: 0.03sMultiple Context Managers
You can manage several resources in one withwith statement by separating them with commas.
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())
# both files are closed automaticallywith open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())
# both files are closed automaticallyVisualize it
A context manager guarantees cleanup: no matter how the withwith body finishes, __exit____exit__ is called.
flowchart TD
A(["Enter with block"]) --> B["__enter__(): acquire resource (e.g. open file)"]
B --> C["Run with body"]
C --> D{"Body raises?"}
D -- no --> E["Body completes normally"]
D -- yes --> F["Exception raised"]
E --> G["__exit__(): release resource (e.g. close file)"]
F --> G
G --> H(["Continue program"])
Conclusion
Context managers automate resource setup and cleanup through the withwith statement, guaranteeing teardown even when errors occur. Any object with __enter____enter__ and __exit____exit__ methods is a context manager; __enter____enter__ sets up and returns the resource, while __exit____exit__ cleans up and decides whether to suppress exceptions. For lighter cases, the @contextmanager@contextmanager decorator from contextlibcontextlib turns a generator into a context manager using yieldyield to split setup from cleanup. Reach for withwith whenever you handle files, locks, or connections. For more hands-on examples and in-depth tutorials, explore our resources on Python Central Hub!
Try it: Context Managers Exercises
Exercise 1 – The with Statement
Exercise 2 – Class-Based Context Manager
Exercise 3 – contextlib Generator
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
