Python context managers are objects that define __enter__ and __exit__ to manage setup and teardown around a with block. They guarantee cleanup runs even if an exception occurs, making them ideal for resource management (files, locks, database connections).
Context managers eliminate try/finally boilerplate and make resource handling declarative and clear.
This example implements a context manager for database connections and timing.
# run: python3 context_managers.py # description: custom context manager for resource management import time class DatabaseConnection: def __init__(self, name): self.name = name self.connected = False def __enter__(self): print(f"Connecting to {self.name}...") time.sleep(0.1) # simulate connection time self.connected = True return self def __exit__(self, exc_type, exc_val, exc_tb): print(f"Disconnecting from {self.name}...") self.connected = False if exc_type is not None: print(f"Exception occurred: {exc_type.__name__}: {exc_val}") return False # don't suppress exception def query(self, sql): if not self.connected: raise RuntimeError("Not connected") return f"Result of: {sql}" # Use context manager with DatabaseConnection("MyDB") as db: print(db.query("SELECT * FROM users")) print("Connection closed after with block\n") # Exception handling try: with DatabaseConnection("MyDB") as db: print(db.query("SELECT * FROM users")) raise ValueError("Bad query") except ValueError: print("Caught exception (cleanup still ran)")
Decorator approach (simpler for simple cases):
@contextlib.contextmanager decorator on generator function__enter__ and __exit__
Return value from __enter__:
return self to access the resourceas in with statement
__exit__ parameters:
exc_type: exception class (None if no exception)exc_val: exception instanceexc_tb: exception tracebackTrue to suppress the exception; False to re-raiseGenerator version:
from contextlib import contextmanager @contextmanager def database(name): print(f"Connecting to {name}...") try: yield None # resource goes here finally: print(f"Disconnecting from {name}...")
Nested contexts:
with statements can nest: with A() as a, B() as b:with A() as a: with B() as b:Common uses:
with open(...))with lock:)with db.transaction():)with chdir(...))with timer():)with patch(...):)