Контекстні менеджери у Python – це механізм, що гарантує коректне управління ресурсами, шляхом їх автоматичного виділення і звільнення (наприклад відкриття/закриття файлу або з’єднання з базою даних, тощо). Контекстні менеджери використовуються разом з оператором Python with, котрий забезпечує виконання певних операцій перед входом у блок коду, та після виходу з нього.

Синтаксис конструкції with:

with <вираз> as <змінна>:
    <блок коду>

При цьому частина as ... є необов’язковою.

Поширеним прикладом використання контекстних менеджерів є робота з файлами:

with open('file.txt', 'w') as file:
    file.write('Some text')

Код з прикладу вище відкриває файл, записує до нього дані, після чого закриває файл. Якщо під час запису даних до файлу виникне помилка, контекстний менеджер всеодно закриє файл. Цей код еквівалентний наступному:

file = open('file.txt', 'w')

try:
    file.write('Some text')
finally:
    file.close()

Перевагою використання with у цьому випадку, є гарантоване закриття файлу, незалежно від того як завершиться виконання вкладеного у блок коду.

Реалізація власного контекстного менеджера у Python

Щоб створити власний контекстний менеджер, потрібно визначити клас який містить методи __enter__() та __exit__():

  • Метод __enter__() виконується перед блоком коду всередині інструкції with. Він може робити будь-які підготовчі дії та повертати значення, котре буде прив’язано до змінної, що визначається після ключового слова as.
  • Метод __exit__() викликається після завершення виконання блоку коду всередині with. Його мета виконання завершальних операцій, таких як звільнення ресурсів, обробка винятків, тощо.

Приклад простого контекстного менеджера, який вимірює час виконання блоку коду:

import time


class Timer:
    def __enter__(self):
        print('Start operation')
        self.start_time = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        execution_time = time.time() - self.start_time
        print(f'Execution time: {execution_time:.2f} seconds')


with Timer():
    time.sleep(2)

# Start operation
# Execution time: 2.00 seconds

Оскільки у методі __exit__() можна обробити виняткові ситуації, що виникли під час виконання у блоці with, йому передаються наступні параметри:

  • exc_class – клас винятку
  • exc_value – екземпляр винятку
  • exc_tb – інформація про виняток (traceback)

Якщо під час виконання блоку with не було створено жодного винятку, усім цим параметрам передається значення None.

Створення контекстного менеджера за допомогою contextlib.contextmanager

Також є можливість створити контекстний менеджер використовуючи функцію з декоратором contextlib.contextmanager. Для цього функція повинна бути генератором, який видає об’єкт для використання у блоці with. Все що стоїть перед yield, буде виконуватись перед входом у блок with, а після yield – після виходу з нього.

Приклад реалізації все того ж контекстного менеджера для таймера:

import time
from contextlib import contextmanager


@contextmanager
def timer():
    print('Start operation')
    start_time = time.time()
    yield
    execution_time = time.time() - start_time
    print(f'Execution time: {execution_time:.2f} seconds')


with timer():
    time.sleep(2)

# Start operation
# Execution time: 2.01 seconds

Підсумок

Контекстні менеджери – важлива концепція Python, яка забезпечує зручність, безпеку та ефективність при роботі з такими ресурсами як файли, бази даних, мережеві з’єднання та інші об’єкти, які потребують спеціального управління життєвим циклом.


Мітки: