Для чего нужен менеджер контекста with
Использование менеджера контекста в Python
В этой статье вы узнаете что такое контекстный менеджер, и как он упрощает работу в Python.
Введение
Контекстный менеджер помогает упростить некоторые общие шаблоны управления ресурсами, абстрагируя их функциональность и позволяя их учитывать и повторно использовать. Давайте разберемся в упрощенном варианте.
Причины использования
Программисты время от времени работают с внешними ресурсами, такими как файлы, соединения с базами данных, блокировки и так далее. Контекстные менеджеры позволяют нам управлять этими ресурсами, указывая:
Рассмотрим следующий пример:
Обратите внимание, что я вызываю метод close(), чтобы гарантировать, что файловый дескриптор освобождается каждый раз. Если бы я этого не сделал, наша ОС (операционная система) в конечном итоге исчерпала бы свой разрешенный лимит на открытие файловых дескрипторов.
Однако я напишу более удобочитаемый вариант с помощью контекстного менеджера:
В этом примере open(«temp.txt», «+a») является менеджером контекста, который активируется с помощью оператора with. Обратите внимание, что мне не нужно было явно закрывать документ, контекстный менеджер позаботился об этом за меня. Точно так же в Python есть и другие предопределенные контекстные менеджеры, которые облегчают нашу работу.
Создание менеджера контекста
Существует два способа определения пользовательского контекстного менеджера:
Контекстный менеджер на основе класса
Давайте продолжим с нашим примером и попробуем определить наш собственный контекстный менеджер, который будет эмулировать функцию open():
Обработка ошибок
Пример того как я обрабатываю FileNotFoundError:
Это базовый код обработки ошибок, который должен быть каждый раз, когда вы открываете файл. Давайте попробуем добавить его в наш созданный менеджер контекста:
Изменения в атрибутах метода _exit_:
Контекстные менеджеры на основе функции
Управление контекстом на основе функций осуществляется с помощью библиотеки под названием contextlib, с помощью которого мы можем превратить простую функцию-генератор в контекстный менеджер. Вот как выглядит типичный код:
Обработка файлов
Я заключаю yield в блок try, потому что не знаю, что пользователь собирается делать с объектом open_file.
Заключение
Мы только что рассмотрели введение в контекстные менеджеры, но я чувствую, что это только верхушка айсберга, и для них есть много интересных вариантов использования.
Python. Урок 21. Работа с контекстным менеджером
Контекстные менеджеры позволяют задать поведение при работе с конструкцией with: при входе и выходе из блока. Это упрощает работу с ресурсами в части их захвата и освобождения; транзакциями, когда нужно либо полностью закончить транзакцию, либо откатить ее целиком. Этой теме будет посвящен данный урок.
Работа с контекстным менеджером
Но это не очень хорошее решение, если в процессе работы с файлом (запись, чтение), произошло исключение, то функция close() не будет вызвана, что влечет за собой возможную потерю данных. Для решения этого вопроса воспользуемся обработкой исключения:
Для того, чтобы не писать дополнительный код, связанный с обработкой исключений (это неудобно и об этом можно забыть), можно воспользоваться конструкцией with… as :
Такая конструкция позволяет захватить ресурс (в данном случае файл), выполнить нужный набор операций (запись данных), а перед выходом – освободить ресурс.
Создание своего контекстного менеджера
Перед тем как перейти к примеру, демонстрирующему работу с этими функциями, рассмотрим, что происходит (какие методы и в каком порядке вызываются) в конструкции:
Пример реализации контекстного менеджера
Создадим класс, у объекта которого необходимо вызывать метод post_work() перед прекращением работы с ним:
Пример работы с ResourceForWith и конструкцией with :
Если выполнить этот код, то получим следующий вывод на консоль
Работа с contextlib
Рассмотрим несколько примеров:
В contextmanager можно завернуть работу с файлом :
P.S.
Python. Урок 21. Работа с контекстным менеджером : 2 комментария
Добрый день! Подскажите, как можно реализовать менеджер контекста для измерения времени выполнения какого-либо кода с помощью with и показывать результат измерения после завершения исполнения этого кода.
по типу:
>>> with Timer ():
do_some_long_stuff ()
from contextlib import contextmanager
import time
@contextmanager
def timer():
start_time = time.time()
yield
print(“— %s seconds —” % (time.time() – start_time))
with timer():
for i in range(1000):
junk = list(range(i**2))
print(“–DONE–“)
Оператор with в Python — менеджеры контекста
Оператор Python with очень полезен. Он появился с Python 2.5, и теперь это вездесущая функция, используемая почти каждым приложением.
Самое полезное (фактически единственное!) Он открывает и освобождает ресурсы.
По сути, он обрабатывает открытие и закрытие любых ресурсов, которые могут вам понадобиться для определенной части программы, а затем автоматически закрывает ее.
Зачем нужны контекстные менеджеры в Python?
Рассмотрим сценарий, в котором вы выполняете обработку файлов. В других языках, таких как C, мы должны вручную открывать и закрывать файл следующим образом:
Оператор with теперь автоматически это делает, поэтому вам не нужно каждый раз закрывать файл вручную. У оператора with есть контекст (блок), в котором он действует. Это как объем заявления.
Когда программа выходит из этого контекста, with автоматически закрывается ваш файл. Из-за этого with часто называют диспетчером контекста. Таким образом, можно использовать те же процедуры обработки файлов.
Обратите внимание, насколько это интуитивно понятно. Оператор Python with всегда будет закрывать файл в конце, даже если программа завершилась ненормально даже в контексте или блоке.
Эта функция безопасности рекомендуемый выбор для всех программистов.
Использование
Объект контекста — это объект, который содержит дополнительную информацию о своем состоянии, такую как модуль или область видимости и т. д. Это полезно, поскольку мы можем сохранять или восстанавливать состояние этого объекта.
А теперь идем дальше. После создания объекта контекста он вызывает метод __enter__ dunder для объекта.
Использование as необязательно, особенно если у вас есть ссылка на исходный объект контекста в другом месте.
После этого мы входим во вложенный блок операторов.
После того, как вложенный блок закончился, ИЛИ, если в нем есть исключение, программа всегда выполняет __exit__ для объекта контекста.
Это первая функция безопасности, о которой мы говорили ранее. Итак, что бы ни случилось, мы всегда будем использовать __exit__ для освобождения ресурсов и выхода из контекста.
Наконец, если возможно, __exit__ может быть реализован так, чтобы восстановить состояние объекта контекста, чтобы он вернулся в то состояние, к которому он принадлежал.
Чтобы было понятнее, давайте рассмотрим пример создания нашего собственного диспетчера контекста для класса.
Создание наших собственных менеджеров контекста для нашего класса
Рассмотрим приведенный ниже класс, для которого у нас будет собственный диспетчер контекста для обработки файлов.
Внимательно изучите методы класса. У нас есть метод __init__ для нашего обработчика, который устанавливает начальное состояние объектов контекста и соответствующих переменных.
Теперь метод __enter__ dunder сохраняет состояние объекта и открывает файл. Теперь мы внутри блока.
Хорошо, похоже, у нас нет ошибок. Мы только что реализовали собственные контекстные менеджеры для нашего Custom Class. Теперь есть другой подход к созданию диспетчера контекста, который использует генераторы.
Однако это обычно не рекомендуется, если вы точно не знаете, что делаете, поскольку вы должны сами обрабатывать исключения.
Есть два разных метода для менеджера контекста. Эти методы —
Метод __enter __()
Метод __enter __() используется для входа в контекст времени выполнения. Он вернет либо текущий объект, либо другой связанный объект. Возвращаемое значение привязывается к идентификатору в качестве предложения оператора with.
Метод __exit __ (exc_type, exc_val, exc_tb)
Метод __exit __() используется для возврата результата логического типа. Это указывает на любое исключение, которое произошло. Если есть одно исключение для оператора with, оно перейдет в конец блока.
Как устроен with
Контекстные менеджеры (оператор with ) встречаются там, где перед совершением действия нужно что-то настроить, а после – прибраться. Например, чтобы прочитать файл, мы используем такой контекстный менеджер:
Здесь “настройкой” является открытие файла, а “уборкой” – его закрытие. Сейчас мы разберемся, как утроены контекстные менеджеры в Питоне.
Свой контекстный менеджер
Заменим нетипичную конструкцию with. as более распространенной try. finally :
Напишем собственный контекстный менеджер, который создаст книгу в Excel, а по завершении работы с ней – сохранит. Другими словами, мы хотели бы использовать его вот так:
Соответственно, использовать его можно так:
И этот код действительно создает книгу и записывает число 1 в первую ячейку. Проблема только в том, что так контекстные менеджеры не пишут.
Свой контекстный менеджер, ver. 2.0
Теперь заметим, что генератор create_workbook содержит всю предметно-ориентированную логику. Поэтому класс CreateWorkbook можно обобщить так, чтобы он от нее не зависил и работал с любыми генераторами. Заодно переименуем его в ContextManager :
В конструкторе ContextManager мы принимаем генератор, при вызове объекта записываем переданные генератору параметры, а при вызове __enter__ вызываем генератор. Теперь класс ContextManager можно использовать следующим образом:
Вызов ContextManager(create_workbook)(‘workbook_name.xlsx’) выглядит некрасиво. Исправим это:
И это еще не всё. Если после yield workbook пользовательский код поднимит исключение, мы не сохраним книгу. Чтобы этого избежать, обернем это выражение в try. finally :
А вот так контекстные менеджеры писать принято.
Резюме
Дальнейшее чтение
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Тип contextmanager, контекстный менеджер
Оператор with в Python поддерживает концепцию контекста среды выполнения, определенного контекстным менеджером. Типичные области применения контекстных менеджеров включают сохранение и восстановление различных типов глобального состояния, блокировку и разблокировку ресурсов, закрытие открытых файлов и т. д.
Содержание:
Синтаксис оператора контекста with :
Как работает менеджер контекста with :
Если последовательность команд была завершена по любой причине, кроме исключения, то возвращаемое значение из __exit__() игнорируется, и выполнение продолжается.
При наличии нескольких контекстных менеджеров, то они обрабатываются так, как если бы несколько операторов with были вложенными:
С версии Python 3.10 поддерживается использование круглых скобок для написания нескольких диспетчеров. Это позволяет форматировать длинную коллекцию диспетчеров контекста в несколько строк аналогично тому, как это можно с операторами импорта. Например, теперь действительны все эти примеры:
Допускается использовать конечную запятую в конце заключенной группы:
Реализация/протокол менеджера контекста.
Протокол контекстных менеджеров реализован с помощью пары методов, которые позволяют определяемым пользователем классам определять контекст среды выполнения, который вводится до выполнения тела инструкции и завершается при завершении инструкции:
contextmanager.__enter__() :
contextmanager.__exit__(exc_type, exc_val, exc_tb) :
Упрощенное создание менеджеров контекста.
Многие контекстные менеджеры, например, файлы и контексты на основе генераторов будут одноразовыми объектами. После вызова метода __exit__() менеджер контекста больше не будет находиться в работоспособном состоянии (например, файл был закрыт или базовый генератор завершил выполнение).
