Context Managers and the 'with' Statement
Context managers handle resource setup and cleanup automatically using the 'with' statement. They ensure proper resource management even when errors occur.
File Handling: The Classic Example
# Without context manager (bad - file may not close)
f = open('file.txt', 'r')
data = f.read()
f.close() # Might not execute if error occurs
# With context manager (good - file always closes)
with open('file.txt', 'r') as f:
data = f.read()
# File automatically closed here, even if exception occurs
Database Connections
import sqlite3
# Connection automatically commits and closes
with sqlite3.connect('database.db') as conn:
cursor = conn.cursor()
cursor.execute("INSERT INTO users (name) VALUES ('Alice')")
# Automatically commits if no error
# Automatically rolls back if error occurs
# Connection closed here
Multiple Context Managers
# Python 3.1+: Multiple resources
with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile:
for line in infile:
outfile.write(line.upper())
# Both files closed automatically
Creating Your Own Context Manager
Using a Class
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, *args):
self.end = time.time()
print(f"Elapsed: {self.end - self.start:.2f} seconds")
with Timer():
# Code to time
sum([i**2 for i in range(1000000)])
# Prints: Elapsed: 0.15 seconds
Using @contextmanager Decorator
from contextlib import contextmanager
@contextmanager
def temporary_setting(name, value):
# Setup
import os
old_value = os.environ.get(name)
os.environ[name] = value
try:
yield # Code block runs here
finally:
# Cleanup
if old_value is None:
del os.environ[name]
else:
os.environ[name] = old_value
with temporary_setting('DEBUG', 'true'):
print(os.environ['DEBUG']) # 'true'
# DEBUG setting restored to original value
Practical Use Cases
# Temporarily change directory
from contextlib import contextmanager
import os
@contextmanager
def working_directory(path):
old_dir = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(old_dir)
with working_directory('/tmp'):
# Work in /tmp
pass
# Back to original directory
When to Use Context Managers
- File operations (reading, writing)
- Database connections and transactions
- Network sockets
- Locks and threading resources
- Temporary state changes
Pro Tip: Always use 'with' for files and database connections. It guarantees cleanup even during exceptions. Use @contextmanager for simple cases; use __enter__ and __exit__ for complex resource management.
← Back to Python Tips