Время прочтения: 3 мин.
Наверняка каждый, кто использует в своей работе python, сталкивался с ключевым словом with. Классическим примером будет являться работа с файлами:
with open(path, 'w') as file:
file.write(data)
Преимущество использования ключевого слова with перед вызовом функции open() в том, что функция file.close() вызовется автоматически и освободит занятые ресурсы после того, как отработает код. С первого взгляда кажется, что экономится лишь лишняя строка кода в нашей программе, но это не совсем так, главной особенностью конструкции with является то, что финальный код (в данном случае file.close()) вызывается гарантированно, даже в том случае, если при обработке интерпретатором строк внутри конструкции произойдет ошибка.
Важно заметить, что python позволяет разработчикам писать собственные контекстные менеджеры, и оборачивать в эту конструкцию практически любой объект, будь то функция или же целый класс с множеством методов. Этим свойством необходимо уметь пользоваться, если в вашей программе есть участки, выполнение которых критично для стабильной работы.
Для примера возьмем следующую задачу – наша программа должна запускать стороннее консольное приложение при помощи модуля subprocess, выполнять измерение времени выполнения этого приложения, а выполнение метода subprocess.terminate() мы сделаем автоматическим и гарантированным.
Код описывающий менеджер контекста:
class ForWith:
def __init__(self, a):
self.start_time = time.time()
self.a = a
self.b = subprocess.Popen(self.a)
def __enter__(self):
return self.b
def __exit__(self, *args):
print(time.time()-self.start_time)
self.b.terminate()
Код для запуска нашего приложения:
program = [path_exe, path_data]
with ForWith(program) as p:
code = p.wait()
print('exit code =', code)
Рассмотрим приведенный выше код:
В созданном нами классе «ForWith» описано три магических метода:
__init__ — служит для описания атрибутов нашего класса, атрибут self.a передает аргументы вызова, self.b – это экземпляр класса subprocess.Popen
__enter__ — в этой функции описываются методы, вызываемые при старте контекстного менеджера. Объект, возвращаемый данной функцией, присваивается переменной в конце выражения with ForWith(*args) as p:. В нашем примере переменной p присвоится атрибут b, который в свою очередь – экземпляр класса subprocess.Popen. Теперь в нашем блоке кода, заключенном в менеджер контекста, мы можем вызывать методы класса subprocess.Popen обращаясь к переменной p, например: p.wait() или p.communicate().
__exit__ — магический метод который будет вызван в завершении конструкции with, или в случае возникновения ошибки, после нее. В этот метод передаются параметры завершения процесса, а код этого метода будет выполнен гарантированно. В нашем примере метод __exit__ выводит на экран время выполнения нашего приложения и вызывает функцию subprocess.terminate(), закрывающую наше приложение и освобождающую ресурсы.
Использование этого инструмента может помочь в ряде задач: например, мы можем закрывать соединение с сервером базы данных после выполнения запросов, можем в критический момент записать какой-либо логфайл или выполнить сохранение датафрейма на диск.