В этой статье вы узнаете что такое контекстный менеджер, и как он упрощает работу в Python.
Введение
Контекстный менеджер помогает упростить некоторые общие шаблоны управления ресурсами, абстрагируя их функциональность и позволяя их учитывать и повторно использовать. Давайте разберемся в упрощенном варианте.
Причины использования
Программисты время от времени работают с внешними ресурсами, такими как файлы, соединения с базами данных, блокировки и так далее. Контекстные менеджеры позволяют нам управлять этими ресурсами, указывая:
- Что делать, когда мы получаем доступ к ресурсу
- Что делать, когда доступ к ресурсу уже не нужен
Рассмотрим следующий пример:
for num in range(333): a = open("temp.txt", "+a") a.write(num) a.close()
Обратите внимание, что я вызываю метод close(), чтобы гарантировать, что файловый дескриптор освобождается каждый раз. Если бы я этого не сделал, наша ОС (операционная система) в конечном итоге исчерпала бы свой разрешенный лимит на открытие файловых дескрипторов.
Однако я напишу более удобочитаемый вариант с помощью контекстного менеджера:
for num in range(444): with open("temp.txt", "+a") as a: a.write(num)
В этом примере open(«temp.txt», «+a») является менеджером контекста, который активируется с помощью оператора with. Обратите внимание, что мне не нужно было явно закрывать документ, контекстный менеджер позаботился об этом за меня. Точно так же в Python есть и другие предопределенные контекстные менеджеры, которые облегчают нашу работу.
Создание менеджера контекста
Существует два способа определения пользовательского контекстного менеджера:
- Определение на основе класса
- Определение на основе функций
Контекстный менеджер на основе класса
Давайте продолжим с нашим примером и попробуем определить наш собственный контекстный менеджер, который будет эмулировать функцию open():
class FileOpen: def __init__(self, somefilename, filemode): self.somefilename = somefilename self.filemode = filemode def __enter__(self): print("run __enter__ method") self.openfile = open(self.somefilename, self.filemode) return self.openfile def __exit__(self, *args): print("run __exit__ method") self.openfile.close() for num in range(555): with FileOpen("temp.txt", "+a") as a: a.write(num)
- В методе _enter_ мы говорим что делать, когда я получаю доступ к ресурсам, т.е. получаем объект открытого файла.
- Метод _exit_ определяет, что делать, когда я выхожу из контекстного менеджера, то есть закрываю файл.
- Вы можете наблюдать за тем, как _enter_ и _exit_ запускаются при каждом цикле.
Обработка ошибок
Пример того как я обрабатываю FileNotFoundError:
for num in range(666): try: with FileOpen("temp.txt", "+a") as a: a.write(num) except FileNotFoundError: print("Невозможно открыть файл")
Это базовый код обработки ошибок, который должен быть каждый раз, когда вы открываете файл. Давайте попробуем добавить его в наш созданный менеджер контекста:
class FileOpen: def __init__(self, other_txt_file, filemode): self.other_txt_file = other_txt_file self.filemode = filemode def __enter__(self): print("run __enter__ method") try: self.opened_file = open(self.other_txt_file, self.filemode) except FileNotFoundError: print("Невозможно открыть файл") return self.opened_file def __exit__(self, exc_type, exc_value, exc_traceback): print("run __exit__ method") if exc_type is None: self.opened_file.close() return True else: return True
Изменения в атрибутах метода _exit_:
- exc_type — это тип класса ошибок, который вы получите при обработке ошибок в _enter_ (в данном случае AttributeError).
- exc_value — это значение ошибки, которое вы получите при обработке ошибок в _enter_.
- exc_traceback — это трассировка ошибки, которую вы получите при обработке ошибок в _enter_.
- Я возвращаю True, чтобы подавить трассировку ошибок (не путать с параметром exc_traceback).
Контекстные менеджеры на основе функции
Управление контекстом на основе функций осуществляется с помощью библиотеки под названием contextlib, с помощью которого мы можем превратить простую функцию-генератор в контекстный менеджер. Вот как выглядит типичный код:
from contextlib import contextmanager @contextmanager def test_func(): print("run method __enter__") yield {} print("run method __exit__") with test_func() as f: print(f)
- @contextmanager декоратор используется для превращения любой функции генератора в контекстный менеджер.
- yield работает как разделитель между частями _enter_ и _exit_ контекстного менеджера.
Обработка файлов
from contextlib import contextmanager @contextmanager def file_open(txt_file, mode): open_file = open(txt_file, mode) try: yield open_file except: print("Произошла ошибка открытия файла") finally: open_file.close() with file_open("temp.txt", "+a") as a: content = a.readlines()
Я заключаю yield в блок try, потому что не знаю, что пользователь собирается делать с объектом open_file.
Заключение
Мы только что рассмотрели введение в контекстные менеджеры, но я чувствую, что это только верхушка айсберга, и для них есть много интересных вариантов использования.
Хорошая информация для ознакомление основами контекстного менеджера)
спасибо большое, я старался максимально простым языком написать 🙂