Working with contextual managers in Python: Resource Management using the structure of WITH for safe opening and closing files.

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

A self-study guide for Python 3 compiled from the materials on this site. Primarily intended for those who want to learn the Python programming language from scratch.

Context managers in Python are a powerful tool for managing resources and the context of code execution. They provide automatic initialization and cleanup operations, which makes the code safer and more reliable.

What are context managers

Context managers are objects that define the execution context for the with statement. They ensure that certain operations are performed before and after the code block is executed, even if an exception occurs during execution.

The main advantages of using context managers:

  • Automatic resource management
  • Guaranteed performance of cleaning operations
  • Improving code readability and reliability
  • Preventing memory and resource leaks

Syntax of context managers

with <context_manager_expression> as <variable>:
    <body>

Syntax Components:

  • an expression that creates a context manager object
  • a variable for storing an object returned by the __enter__
  • method
  • a block of code executed in a managed context

Working with files through context managers

A classic example of using — working with files:

# File reading
with open("example.txt", "r", encoding="utf-8") as file:
    content = file.read()
    print(content)
# The file is automatically closed, even if an exception occurs

# Writing to a file
with open("output.txt", "w", encoding="utf-8") as file:
    file.write("Sample text")
file.write("\second line")

Creating your own context managers

Class method

To create a context manager, a class must implement two magic methods:

class MyContextManager:
    def __init__(self, name):
        self.name = name
        
    def __enter__(self):
        print(f"Entering the context: {self.name }")
        return self
        
    def __exit__(self, exc_type, exc_value, traceback):
        print(f"Out of context: {self.name }")
# Return False to pass exceptions on
        return False

# Usage
with MyContextManager("Test Context") as manager:
    print("Execution inside the context")

Using the contextmanager decorator

from contextlib import contextmanager

@contextmanager
def my_context():
print("Context setting")
try:
        yield "Value from context"
    finally:
        print("Clearing the context")

# Application
with my_context() as value:
    print(f"Received value: {value}")

Practical examples of context managers

Managing directories

import os

class ChangeDirectory:
    def __init__(self, new_path):
        self.new_path = new_path
        self.original_path = None

    def __enter__(self):
        self.original_path = os.getcwd()
        os.chdir(self.new_path)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        os.chdir(self.original_path)

# Usage
with ChangeDirectory("/tmp"):
print(f"Current directory: {os.getcwd()}")
# Performing operations in a new directory
# Automatic return to the source directory

Working with the database

import sqlite3

class DatabaseConnection:
    def __init__(self, db_path):
        self.db_path = db_path
        self.connection = None

    def __enter__(self):
        self.connection = sqlite3.connect(self.db_path)
        return self.connection

    def __exit__(self, exc_type, exc_value, traceback):
        if self.connection:
            if exc_type is None:
                self.connection.commit()
            else:
                self.connection.rollback()
            self.connection.close()

# Usage
with DatabaseConnection("example.db") as conn:
    cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER, name TEXT)")
cursor.execute("INSERT INTO users VALUES (1, 'Ivan')")
# Automatic commit and connection closure

Measurement of execution time

import time

class Timer:
    def __enter__(self):
        self.start_time = time.time()
        return self
        
    def __exit__(self, exc_type, exc_value, traceback):
self.end_time =time.time()
print(f"Run time: {self.end_time - self.start_time:.4f} seconds")

# Usage
with Timer():
# The code whose execution time needs to be measured
    time.sleep(2)
    result = sum(range(1000000))

Built-in context managers

Python provides several built-in context managers:

suppress exceptions

from contextlib import suppress

with suppress(FileNotFoundError):
    with open("несуществующий_файл.txt ") as f:
        content = f.read()
# The FileNotFoundError exception will be suppressed

redirect_stdout redirecting output

from contextlib import redirect_stdout
import io

output = io.StringIO()
with redirect_stdout(output):
print("This text will be redirected")
print("This one too")

captured_output = output.getvalue()
print(f"Captured output: {captured_output}")

Exception handling in context managers

The __exit__ method receives information about exceptions:

class ExceptionHandler:
    def __enter__(self):
        return self
        
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is not None:
            print(f"Exception processed: {exc_type.__name__}: {exc_value}")
return True # Suppressing the exception
        return False

# Usage
with ExceptionHandler():
    raise ValueError("Test exception")
print("Code continues to execute")

Multiple context managers

# Method 1: Nested with
with open("input.txt") as input_file:
    with open("output.txt", "w") as output_file:
        data = input_file.read()
        output_file.write(data.upper())

# Method 2: Multiple managers in one with
with open("input.txt") as input_file, \
     open("output.txt", "w") as output_file:
    data = input_file.read()
    output_file.write(data.upper())

Best usage practices

  1. Always use context managers to work with resources (files, database connections, network connections)

  2. Handle exceptions in the __exit__ method if necessary

  3. Use @contextmanager for simple cases instead of creating full-fledged classes

  4. Don't forget about finally in the try-yield-finally blocks when using @contextmanager

  5. Document the behavior of your context managers, especially exception handling

Context managers are an important tool for writing reliable and secure Python code. They ensure proper resource management and make the code more readable and maintainable.

 

categories

  • Introduction to Python
  • Python Programming Basics
  • Control Structures
  • Data Structures
  • Functions and Modules
  • Exception Handling
  • Working with Files and Streams
  • File System
  • Object-Oriented Programming (OOP)
  • Regular Expressions
  • Additional Topics
  • General Python Base