Menu
Courses / python advanced / Context Managers & the with Statement

Context Managers & the with Statement

03 / 10 Part of python advanced




 

    🎓 Advanced · Topic 03
 

 


    Context Managers & the with Statement
 


 


    Guarantee resource cleanup — files, locks, database connections, timers — even when exceptions occur, using Python's context manager protocol.
 


 

    ⏱ ~45 min
    🔴 Advanced
    🛡️ Resource safety
 


 
 

   

     
     

The Context Manager Protocol


   

   

Any object implementing __enter__ and __exit__ can be used with with. __exit__ is always called — even if an exception is raised.


   

     

        class_cm.py
     

     
class Timer:
    import time

    def __enter__(self):
        self.start = self.time.perf_counter()
        return self    # bound to 'as' variable

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.elapsed = self.time.perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.4f}s")
        return False  # don't suppress exceptions

with Timer() as t:
    total = sum(range(1_000_000))
# Elapsed: 0.0381s

   

 


 
 

   

     
     

@contextmanager — Generator Shortcut


   

   

The contextlib.contextmanager decorator lets you write context managers as simple generator functions — code before yield is setup, after is teardown.


   

     

        contextlib_cm.py
     

     
from contextlib import contextmanager
import sqlite3

@contextmanager
def db_transaction(path):
    conn   = sqlite3.connect(path)
    cursor = conn.cursor()
    try:
        yield cursor          # ← body of 'with' runs here
        conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

with db_transaction("app.db") as cur:
    cur.execute("INSERT INTO users VALUES (?, ?)", (1, "Alice"))

   

   

      📌 Exactly one yield
     

A @contextmanager generator must yield exactly once. The yielded value is bound to the as variable. Yielding nothing is fine — just write yield.


   

 


 
 

   

     
     

Useful contextlib Utilities


   

   

     

        suppress(*exc)
       

Silently ignore specified exceptions


     

     

        nullcontext()
       

A no-op context manager, useful for optional contexts


     

     

        ExitStack()
       

Dynamically manage multiple context managers


     

     

        redirect_stdout()
       

Temporarily redirect output to a file or StringIO


     

   

   

     

        contextlib_utils.py
     

     
from contextlib import suppress, ExitStack

# Silently ignore FileNotFoundError
with suppress(FileNotFoundError):
    open("missing.txt")

# Open a dynamic number of files
files = ["a.txt", "b.txt", "c.txt"]
with ExitStack() as stack:
    handles = [stack.enter_context(open(f)) for f in files]
    # all files closed automatically