🔒 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

PatternUse CaseImplementation
File handlingResource cleanupwith open()
Custom resourceSpecific cleanup__enter__/__exit__
DecoratorSimple cases@contextmanager
Multiple resourcesComplex scenariosExitStack
Error handlingGraceful failuresTry/except in __exit__
TimingPerformance monitoring@contextmanager with timing

🎯 Key Takeaways

Was this helpful?

😔Poor
🙁Fair
😊Good
😄Great
🤩Excellent