Контекстні менеджери у 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, яка забезпечує зручність, безпеку та ефективність при роботі з такими ресурсами як файли, бази даних, мережеві з’єднання та інші об’єкти, які потребують спеціального управління життєвим циклом.