1.3.4 · Coding › Python Intermediate
Ek context manager basically ek aisa object hai jo jaanta hai kaise kuch setup karna hai aur baad mein kaise clean up karna hai — chahe beech mein kuch bhi ho jaye . with statement Python ka ek promise hai: "Main tumhara cleanup zaroor karunga, chahe andar ka code crash hi kyu na ho jaye." Bas yahi poora idea hai. Files, locks, DB connections, timers — sab ek hi pattern follow karte hain.
with open ( "data.txt" ) as f: # f = open(...).__enter__()
data = f.read()
# f.__exit__(...) runs here, closing the file — even if read() raised
with ke bina, tumhe cleanup khud yaad rakhni padti hai aur har cheez ko try/finally mein wrap karna padta hai. Log bhool jaate hain. File close karna bhool gaye toh ek handle leak ho jaata hai; lock release karna bhool gaye toh program deadlock ho jaata hai. Context managers cleanup ko setup ke saath rakh dete hain, toh bhoolna possible hi nahi — language khud guarantee karti hai.
Yeh statement
with EXPR as VAR :
BODY
bilkul equivalent hai is de-sugared form ke:
mgr = EXPR
VAR = mgr. __enter__ () # ← return value goes to `as VAR`
try :
BODY
except BaseException :
# __exit__ gets the live exception info
if not mgr. __exit__ ( * sys.exc_info()):
raise # re-raise unless __exit__ returned True
else :
mgr. __exit__ ( None , None , None ) # no exception → all three args are None
Intuition Why this exact shape?
__enter__ ek baar chalta hai, body se pehle — yahi "setup" hai.
try guarantee karta hai ki __exit__ kuch bhi ho chalega — yahi "guaranteed cleanup" hai.
__exit__ ko bataya jaata hai ki exception aayi ki nahi (uske 3 args ke through), toh woh decide kar sakta hai: log kare? swallow kare? alag tarike se clean up kare?
__exit__ ka return value ek "kya exception swallow karun?" flag hai. True return karo → exception suppress. False/None return karo → exception propagate hoti hai.
Worked example A timer context manager (class form)
import time
class Timer :
def __enter__ (self):
self .start = time.perf_counter()
return self # so `as t` gives us the Timer
def __exit__ (self, exc_type, exc_value, tb):
self .elapsed = time.perf_counter() - self .start
print ( f "Took {self .elapsed :.4f } s" )
return False # don't swallow exceptions
with Timer() as t:
sum ( range ( 10_000_000 ))
# prints: Took 0.18s
return self kyun? Kyunki hum chahte hain ki t Timer object ho taaki baad mein t.elapsed padh sakein. Agar kuch return nahi kiya, toh t None hoga.
return False kyun? Hum chahte hain ki block ke andar ki errors phir bhi propagate hon — ek timer bugs ko hide nahi karna chahiye.
Worked example Suppressing a specific error
class ignore :
def __init__ (self, * exc): self .exc = exc
def __enter__ (self): return self
def __exit__ (self, et, ev, tb):
# Why check et is not None? An exception only occurred if et is set.
return et is not None and issubclass (et, self .exc)
with ignore( FileNotFoundError ):
open ( "nope.txt" ) # error raised...
print ( "still here" ) # ...but swallowed → this prints
Yeh step kyun? issubclass(et, self.exc) sirf unhi errors ke liye True return karta hai jo humne ignore karne ko kaha tha, toh __exit__ True return karta hai aur with unhe swallow kar leta hai. Baaki errors False return karti hain aur propagate hoti hain. (Yeh essentially contextlib.suppress hai.)
Worked example The easy way:
@contextmanager
from contextlib import contextmanager
@contextmanager
def open_upper (path):
f = open (path)
try :
yield f.read().upper() # everything before yield = __enter__
finally :
f.close() # everything after = __exit__
with open_upper( "data.txt" ) as text:
print (text)
yield kyun? Single yield function ko "setup" (pehle) aur "cleanup" (baad mein) mein split kar deta hai. Yielded value as variable ban jaati hai. finally kyun? Taaki file tab bhi close ho jab body raise kare.
with file object return karta hai, toh f hi manager hai."
Kyun sahi lagta hai: with open(...) as f aur f hai file, aur file hai ek context manager — sab same cheez lagti hai.
Fix: f woh hota hai jo __enter__ return karta hai, manager khud nahi. Files ke liye yeh same object hota hai, lekin upar ke open_upper mein manager ek generator hai aur text ek string hai. Hamesha poocho: "__enter__ kya return karta hai?"
__exit__ se kuch return nahi karna matlab error swallow ho gayi."
Kyun sahi lagta hai: Tumne cleanup kar diya, toh surely error handle ho gayi?
Fix: __exit__ ka None return karna (default) falsy hai → exception propagate hoti hai. Suppress karne ke liye explicitly return True karna padega. Yeh acha hai: silent error-swallowing bugs ka ek bada source hai.
__exit__ nahi chalega agar block raise kare."
Kyun sahi lagta hai: Errors usually baaki ka code skip kar deti hain.
Fix: Yahi toh with ka poora point hai — __exit__ ek try/finally ke through chalta hai, toh yeh hamesha chalta hai. Setup hua, toh cleanup guaranteed hai.
@contextmanager mein __enter__ ke do hisson ke beech return kar sakta hun."
Kyun sahi lagta hai: Yeh ek normal function hai.
Fix: Ek @contextmanager generator ko exactly ek baar yield karna chahiye. Zero yields → RuntimeError: didn't yield; do yields → RuntimeError: generator didn't stop.
Recall Feynman: explain to a 12-year-old
Socho jaise library se book borrow karna. Jab tum andar jaate ho (__enter__) toh book checkout karte ho. Jab bahar jaate ho (__exit__) toh wapas karna zaroor padega — chahe raaste mein gir bhi jao. with block ek librarian ki tarah hai jo darwaze par khada hai aur pakka karta hai ki tum book lekar kabhi bahar nahi jaoge. Tumhe yaad rakhne ki zaroorat nahi; rule khud kar deta hai.
Mnemonic Remember the pair
"ENTER sets, EXIT resets." Aur EXIT ke 3 arguments crash report likhte hain: T ype, V alue, T raceback (TVT). T rue return karo toh error T oss ho jaati hai.
Kaunse do methods kisi object ko context manager banate hain?
as variable ki value kahan se aati hai?
__exit__ se True return karne par kya hota hai?
Kya __exit__ chalta hai agar body raise kare?
What two dunder methods define a context manager? __enter__ and __exit__
What is bound to the as variable in a with statement? The return value of __enter__
What are the three arguments passed to __exit__? exc_type, exc_value, traceback (None×3 if no exception)
What does __exit__ returning a truthy value do? Suppresses (swallows) the exception that occurred in the block
Does __exit__ run when the with body raises an exception? Yes — it always runs, like a finally block
In @contextmanager, what splits setup from cleanup? A single yield; code before is __enter__, code after (in finally) is __exit__
Why put cleanup in a finally inside a @contextmanager? So cleanup runs even if the with-block body raises an exception
What is with EXPR as VAR: de-sugared to? mgr=EXPR; VAR=mgr.enter (); try BODY; exit runs in try/finally with exc info
contextlib helper that ignores given exception types? contextlib.suppress(*exceptions)
Exception handling — try-except-finally — with try/finally ke upar sugar hai.
Decorators — @contextmanager ek decorator hai jo generator ko manager mein convert karta hai.
Generators and yield — @contextmanager ke peeche single-yield trick hai.
File I/O — canonical with open(...) use case.
Threading and locks — with lock: release guarantee karta hai.
Dunder methods — __enter__/__exit__ protocol family ka hissa hain.
Files, locks, timers, DB conns