The Python with Statement — It’s More Powerful Than You Think
From managing resources to building your own context managers, here’s how the with statement can simplify your code, reduce bugs, and make…

Most devs use with
for opening files—and stop there. But this little keyword has powers far beyond cleanup.
The Python with
Statement — It’s More Powerful Than You Think
From managing resources to building your own context managers, here’s how the with
statement can simplify your code, reduce bugs, and make you a more elegant Python developer.
If you thought with
was just for opening files, you're missing out.
When most developers first encounter Python’s with
statement, it’s usually in the context of file handling:
with open('example.txt', 'r') as file:
data = file.read()
Clean. Concise. No need to remember to close the file.
But here’s the thing: the with
statement is much more than just syntactic sugar for file operations.
It’s a powerful feature in Python that helps you write cleaner, safer, and more maintainable code.
In this article, I’ll walk you through what makes with
tick, how it works under the hood, and how you can use it to supercharge your own Python programs — even creating your own with
-enabled objects.
What is the with
Statement, Really?
The with
statement is Python’s built-in way of managing resources.
Resources like:
- Files
- Network connections
- Database sessions
- Locks
- Temporary directories
- External APIs
- And much more…
At its core, with
is powered by a concept called a context manager. A context manager is any object that implements the methods __enter__()
and __exit__()
.
When you write:
with resource as r:
# do something
It’s doing something like this behind the scenes:
r = resource.__enter__()
try:
# do something
finally:
resource.__exit__()
That’s right — with
automatically ensures proper setup and teardown, even if an exception occurs inside the block.
More Than Just Files: Real-World Uses
1. Thread Locks
from threading import Lock
lock = Lock()
with lock:
# Critical section
do_something()
The lock is automatically acquired at the start of the block and released at the end.
No more worrying about forgetting to release it!
2. Database Transactions
If you’re using something like SQLAlchemy:
from sqlalchemy.orm import Session
with Session(engine) as session:
session.add(new_user)
session.commit()
The session is committed if everything goes well.
If not, it rolls back automatically — avoiding potential data corruption.
3. Temporary Files and Directories
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
# Use tmpdir safely
Once the block ends, the temporary directory is cleaned up — zero manual cleanup required.
4. Suppressing Exceptions Gracefully
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove("some_file.txt")
This is a clean and elegant alternative to wrapping code in a try-except block.
Create Your Own Context Manager
Want to harness the power of with
in your own classes? Just define __enter__
and __exit__
.
Here’s a simple example:
class OpenResource:
def __enter__(self):
print("Resource acquired")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Resource released")
with OpenResource() as res:
print("Using the resource")
Output:
Resource acquired
Using the resource
Resource released
Boom. You’ve just built a context manager.
Cleaner Context Managers with contextlib
Python’s contextlib
module lets you write context managers using a simple generator. Here’s how:
from contextlib import contextmanager
@contextmanager
def managed_resource():
print("Setup")
yield
print("Cleanup")
with managed_resource():
print("Inside the block")
This is especially handy for one-off or utility context managers without needing a whole class.
Bonus: Handle Exceptions Like a Pro
The __exit__
method receives three arguments when an exception occurs:
exc_type
: the exception class (e.g.,ZeroDivisionError
)exc_val
: the exception instance (e.g.,ZeroDivisionError('division by zero')
)exc_tb
: the traceback object
If your __exit__
method returns True
, Python will suppress the exception!
class SuppressErrors:
def __enter__(self): pass
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exception suppressed:", exc_val)
return True
with SuppressErrors():
1 / 0
The error is caught, and the program continues — mindfully.
Final Thoughts
The with
statement isn’t just a nicety — it’s a best practice.
It helps manage resources cleanly, reduces boilerplate, and prevents hard-to-find bugs by ensuring proper cleanup.
The next time you catch yourself writing a try-finally block, ask yourself: could I use with
instead?
And once you’re comfortable, start building your own context managers. It’ll not only make your code cleaner — it’ll make you a better Python developer.
Enjoyed this article?
Let me know in the comments or give it a clap if you learned something new!
Follow for more Python deep dives and real-world coding insights.
