Context Managers & the with Statement
Lesson 3 of 6 · View course roadmap
Learn the idea
Every time you write with open(...) as f: you're using a context manager — an object that promises to set something up on entry and clean it up on exit, even if the body raises an exception.
The protocol is two dunder methods:
__enter__(self)— runs at the start of thewithblock; its return value is bound byas__exit__(self, exc_type, exc, tb)— always runs at the end, exception or not
Files, database transactions, locks, temporary directories, mocked tests — anything with a setup/teardown pair belongs in a context manager. The contextlib.contextmanager decorator lets you write one as a generator: code before yield is the entry, code after is the exit.
Where you'll use this
Database transactions (commit/rollback), file handles, thread locks, temporary directories, test mocks (unittest.mock.patch) and even changing directories — professional code wraps every setup/teardown pair in with so cleanup survives exceptions.
Common mistakes
- Returning
Truefrom__exit__without meaning to — that swallows the exception and the caller never learns something failed. - Doing risky work in
__init__instead of__enter__— resources should be acquired when the with block starts, not when the object is created. - Forgetting that
asbinds__enter__'s return value — returnselfunless you have a better handle to give.
Pro tip
One with statement can manage several resources: with open(a) as f, open(b) as g:. And contextlib.suppress(FileNotFoundError) replaces a try/except/pass in one readable line.
Try it yourself
The lesson example is loaded and ready — press Run, then change something and run it again. Breaking it is part of learning. Want a clean slate? Tap “New blank”.
Output appears here…
Pass the challenge +40 XP
Write a class Tag whose constructor takes a tag name. As a context manager it prints <b> on enter and </b> on exit (for name "b"). Use with Tag("b"): around a line printing bold text.
<b> bold text </b>
__enter__ prints f"<{self.name}>" and returns self; __exit__(self, exc_type, exc, tb) prints f"</{self.name}>".
class Tag:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f"<{self.name}>")
return self
def __exit__(self, exc_type, exc, tb):
print(f"</{self.name}>")
return False
with Tag("b"):
print("bold text")
Run your code to check it…
Check your understanding
Your notes (saved on this device)
Tip: use ← and → to move between lessons, ⌘K to search everything.