Context Managers
Context Managers
Section titled “Context Managers”Context managers handle setup and teardown automatically — most commonly used for file I/O, database connections, and locks.
The with Statement
Section titled “The with Statement”# Without context managerf = open("file.txt", "r")try: data = f.read()finally: f.close()
# With context manager — cleaner and saferwith open("file.txt", "r") as f: data = f.read()# f is automatically closed here, even if an exception occursMultiple Context Managers
Section titled “Multiple Context Managers”with open("input.txt") as fin, open("output.txt", "w") as fout: fout.write(fin.read())How It Works: __enter__ and __exit__
Section titled “How It Works: __enter__ and __exit__”class ManagedResource: def __enter__(self): print("Acquiring resource") return self # value bound to 'as' variable
def __exit__(self, exc_type, exc_val, exc_tb): print("Releasing resource") # Return True to suppress exceptions, False to propagate return False
with ManagedResource() as r: print("Using resource")# Output:# Acquiring resource# Using resource# Releasing resourceException Handling in __exit__
Section titled “Exception Handling in __exit__”class SafeDiv: def __enter__(self): return self
def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is ZeroDivisionError: print("Caught division by zero!") return True # suppress the exception return False
with SafeDiv(): result = 10 / 0 # exception suppressedprint("Continues here")contextlib.contextmanager
Section titled “contextlib.contextmanager”The easiest way to create a context manager using a generator:
from contextlib import contextmanager
@contextmanagerdef timer(): import time start = time.time() try: yield # code inside 'with' block runs here finally: elapsed = time.time() - start print(f"Elapsed: {elapsed:.3f}s")
with timer(): sum(range(1_000_000))contextlib.suppress
Section titled “contextlib.suppress”Suppress specific exceptions:
from contextlib import suppress
with suppress(FileNotFoundError): open("nonexistent.txt")# No exception raised — silently ignoredcontextlib.redirect_stdout
Section titled “contextlib.redirect_stdout”from contextlib import redirect_stdoutimport io
f = io.StringIO()with redirect_stdout(f): print("This goes to the buffer")
output = f.getvalue()print(output) # "This goes to the buffer\n"Database Connection Pattern
Section titled “Database Connection Pattern”from contextlib import contextmanagerimport sqlite3
@contextmanagerdef get_db(path): conn = sqlite3.connect(path) try: yield conn conn.commit() except Exception: conn.rollback() raise finally: conn.close()
with get_db("app.db") as db: db.execute("INSERT INTO users VALUES (?, ?)", (1, "Alice"))Threading Lock
Section titled “Threading Lock”import threading
lock = threading.Lock()
with lock: # critical section — lock acquired automatically shared_resource += 1# lock released automatically