🔒 Using Context Managers
Context managers provide a clean way to handle resources that need setup and cleanup. The with
statement ensures proper resource management, even when errors occur.
# Basic context manager usage
with open('example.txt', 'r') as file:
content = file.read()
print(content)
# File is automatically closed here
🎯 Context Manager Benefits
Context managers provide several advantages for resource management.
File Operations with Context Managers
# Safe file operations with context managers
def safe_file_operations():
# Reading files
try:
with open('data.txt', 'r') as file:
lines = file.readlines()
print(f"Read {len(lines)} lines")
except FileNotFoundError:
print("File not found")
# Writing files
with open('output.txt', 'w') as file:
file.write("Hello, context managers!")
file.write("\nAutomatic cleanup!")
safe_file_operations()
Multiple File Handling
# Working with multiple files
def process_multiple_files():
try:
# Multiple files in one context
with open('input.txt', 'r') as infile, open('backup.txt', 'w') as outfile:
content = infile.read()
outfile.write(content)
print("File copying completed")
except FileNotFoundError:
print("Input file not found")
process_multiple_files()
Custom Context Manager Class
# Custom context manager using class
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
print(f"Opening {self.filename}")
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_value, traceback):
print(f"Closing {self.filename}")
if self.file:
self.file.close()
# Handle exceptions
if exc_type:
print(f"Exception occurred: {exc_value}")
return False # Don't suppress exceptions
# Using custom context manager
with FileManager('test.txt', 'w') as file:
file.write("Custom context manager!")
Decorator-Based Context Manager
from contextlib import contextmanager
# Context manager using decorator
@contextmanager
def managed_resource(resource_name):
print(f"Acquiring {resource_name}")
resource = f"Resource: {resource_name}"
try:
yield resource
finally:
print(f"Releasing {resource_name}")
# Using decorated context manager
with managed_resource("database connection") as resource:
print(f"Using {resource}")
🔍 Advanced Context Managers
Python allows sophisticated context manager implementations.
Timing Operations
import time
from contextlib import contextmanager
# Timing context manager
@contextmanager
def timer(operation_name):
"""Context manager for timing operations"""
start_time = time.time()
print(f"Starting {operation_name}")
try:
yield
finally:
end_time = time.time()
duration = end_time - start_time
print(f"{operation_name} took {duration:.2f} seconds")
# Example usage
with timer("file processing"):
time.sleep(0.1) # Simulate work
print("Processing files...")
Directory Management
import os
from pathlib import Path
# Directory context manager
@contextmanager
def change_directory(path):
"""Context manager for temporary directory change"""
original_dir = Path.cwd()
try:
os.chdir(path)
yield Path.cwd()
finally:
os.chdir(original_dir)
# Example usage
print(f"Current directory: {Path.cwd()}")
try:
with change_directory('/tmp'):
print(f"Inside context: {Path.cwd()}")
except FileNotFoundError:
print("Directory not found")
print(f"After context: {Path.cwd()}")
Error Handling
# Error handling context manager
@contextmanager
def error_handler(operation):
"""Context manager with error handling"""
try:
print(f"Starting {operation}")
yield
print(f"Successfully completed {operation}")
except Exception as e:
print(f"Error in {operation}: {e}")
# Log error, send notification, etc.
raise # Re-raise the exception
# Example with error
try:
with error_handler("risky operation"):
raise ValueError("Something went wrong!")
except ValueError:
print("Handled the error")
Multiple Resource Management
from contextlib import ExitStack
# Managing multiple resources with ExitStack
def process_multiple_files(filenames):
"""Process multiple files using ExitStack"""
with ExitStack() as stack:
files = []
for filename in filenames:
try:
file = stack.enter_context(open(filename, 'r'))
files.append(file)
except FileNotFoundError:
print(f"File {filename} not found, skipping")
for file in files:
content = file.read()
print(f"Processed {file.name}: {len(content)} characters")
# Example usage
process_multiple_files(['file1.txt', 'file2.txt', 'file3.txt'])
Thread Safety and Database Connections
import threading
# Lock context manager for threading
class ThreadLock:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
self.lock.acquire()
print("Lock acquired")
return self
def __exit__(self, exc_type, exc_value, traceback):
self.lock.release()
print("Lock released")
return False
# Database connection context manager
@contextmanager
def database_connection(connection_string):
"""Mock database connection context manager"""
print(f"Connecting to {connection_string}")
connection = f"Connection to {connection_string}"
try:
yield connection
except Exception as e:
print(f"Rolling back transaction: {e}")
# Rollback logic here
raise
else:
print("Committing transaction")
# Commit logic here
finally:
print("Closing database connection")
# Cleanup logic here
# Example database usage
with database_connection("postgresql://localhost/mydb") as conn:
print(f"Using {conn}")
# Database operations here
📋 Context Manager Patterns Reference
Pattern | Use Case | Implementation |
---|---|---|
File handling | Resource cleanup | with open() |
Custom resource | Specific cleanup | __enter__/__exit__ |
Decorator | Simple cases | @contextmanager |
Multiple resources | Complex scenarios | ExitStack |
Error handling | Graceful failures | Try/except in __exit__ |
Timing | Performance monitoring | @contextmanager with timing |
🎯 Key Takeaways
Was this helpful?
Track Your Learning Progress
Sign in to bookmark tutorials and keep track of your learning journey.
Your progress is saved automatically as you read.