Для чего нужны контекстные менеджеры
Контекстные менеджеры в Python
Контекстные менеджеры – одна из основных функций языка, которая делает Python уникальным. Оператор with позволяет разработчикам писать свой код в сжатом и понятном виде. Новый вложенный блок даёт визуальный сигнал и облегчает понимание кода. Понимание контекстных менеджеров является ключом к пониманию идеи питонического кода.
Контекстные менеджеры обычно используются для захвата и освобождения ресурсов, но это не единственный случай их использования. Они полезны для унификации общего кода настройки и разрыва или любой пары операций, которые необходимо выполнить до или после действия.
В этой статье будет рассказано о некоторых интересных примерах их использования в реальном коде и автор надеется, что статья станет стимулом для продолжения исследования этой темы.
Коротко о контекстных менеджерах
Говоря простыми словами, контекстные менеджеры упрощают запись блоков try-finally.
Выражение, следующее за ключевым словом with, должно возвращать объект, соответствующий протоколу Context Manager. Это может быть экземпляр класса или вызов функции, который возвращает объект Context Manager, а также реализовывать два специальных метода: __enter__ и __exit__.
Некоторые важные моменты, которые следует помнить.
Если __exit__ возвращает True, исключение будет подавлено.
Простые менеджеры контекста также могут быть написаны с использованием генераторов и декоратора contextmanager:
Надежные деструкторы
Контекстные менеджеры дают нам надежный метод очистки ресурсов. В отличие от других языков OO, таких как C ++ и Java, вызов метода деструктора Python __del__ не всегда гарантируется. Он вызывается только тогда, когда счётчик ссылок на объект достигает нуля. Это может произойти в конце текущей функции или в конце программы или никогда в случае циклических ссылок.
До Python 3.4, если все объекты в эталонном цикле имеют метод __del__, Python не будет вызывать его при их удалении сборщиком мусора. Это связано с тем, что у Python нет безопасного способа узнать, какой порядок удаления этих объекты.
Начиная с Python 3.4, объекты с __del__ теперь могут быть удалены сборщиком мусора. Однако порядок их вызова не определён.
Давайте рассмотрим несколько реальных примеров с контекстными менеджерами.
Убедитесь, что открытый поток закрывается
Объекты StringIO ведут себя одинаково:
Менеджер контекста closing вызывает метод close() для любого объекта, если такой метод в нём существует.
Проверка на возникновение исключения при тестировании
Настройка mocks перед тестированием
При использовании в качестве декоратора mock.patch передает вновь созданный mock объект (возвращаемое значение __enter__) в декорированную функцию. ContextDecorator в Python 3, с другой стороны, не предоставляет доступа к возвращаемому значению метода __enter__.
Синхронизация доступа к общим ресурсам
Оператор with в этом случае вызывает lock.acquire() при входе и lock.release() при выходе.
В потоковом модуле в качестве контекстных менеджеров могут использоваться Lock, RLock, Condition, Semaphore, BoundedSemaphore.
Аналогичный подход можно использовать для блокировки файлов при доступе к ним. Например, диспетчер контекста pidfile использует fcntl.flock() для получения блокировки файла в python-демонах.
Настройка среды выполнения Python
Управление подключениями к базе данных и транзакциями
Обёртка соединений по протоколу
Тайминги выполнения кода
Автоматизация задач администрирования с использованием Fabric
Fabric предоставляет множество интересных менеджеров контекстов для автоматизации развертывания, выполнения локальных и удаленных задач.
Работа с временными файлами
Перенаправление потоков ввода и вывода
В Python 3.4+ диспетчер контекста redirect_stdout и redirect_stderr можно использовать для временного перенаправления потоков stdout и stderr.
redirect_stdout только перенаправляет вызовы stdout из Python, но не из кода библиотеки C.
Чтение и запись в файл inplace
Управление пулом процессов
Библиотека multiprocessing Python предоставляет кучу менеджеров контекстов для управления соединениями, пулами и блокировками ресурсов на уровне ОС.
Резюме
Короче говоря, Context Managers можно использовать в самых разных случаях. Начните использовать их сразу же, когда заметите шаблон «настройка-завершение», чтобы сделать свой код более питоничным.
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»)
Введение
Примеры
Введение в контекстные менеджеры и оператор with
Например, файловые объекты являются контекстными менеджерами. Когда контекст заканчивается, объект файла закрывается автоматически:
Присвоение цели
Многие контекстные менеджеры возвращают объект при вводе. Вы можете назначить этот объект на новое имя в with заявлением.
Например, с помощью подключения к базе данных в with утверждением может дать вам объект курсора:
Файловые объекты возвращаются сами по себе, это позволяет как открыть объект файла, так и использовать его в качестве диспетчера контекста в одном выражении:
Написание собственного менеджера контекста
Если вам необходим только __exit__ метод, вы можете вернуть экземпляр менеджера контекста:
Написание собственного менеджера контекста с использованием синтаксиса генератора
Также можно написать менеджер контекста с использованием синтаксиса генератора благодаря contextlib.contextmanager декоратора:
Несколько контекстных менеджеров
Вы можете открыть несколько менеджеров контента одновременно:
Это имеет тот же эффект, что и вложенные контекстные менеджеры:
Управление ресурсами
__init__() метод устанавливает объект, в данном случае настраивает имя файла и режим открытия файла. __enter__() открывает и возвращает файл и __exit__() просто закрывает его.
Используйте класс File:
Синтаксис
Параметры
Примечания
Научим основам Python и Data Science на практике
Это не обычный теоритический курс, а онлайн-тренажер, с практикой на примерах рабочих задач, в котором вы можете учиться в любое удобное время 24/7. Вы получите реальный опыт, разрабатывая качественный код и анализируя реальные данные.
Скрытые возможности
Введение Примеры Перегрузка оператора Все в Python является объектом. Каждый объект имеет несколько специальных внутренних методов, которые он использует для взаимодействия с другими объектами. Как правило, эти методы следуют __action__ именования. В совокупности, это называется как
Контекстные Менеджеры в Python
Несколько лет назад, в Python 2.5 добавили новое ключевое слово, под названием оператор with. Это новое ключевое слово позволяет разработчику создавать контекстные менеджеры. Но подождите, что же такое контекстный менеджер? Это удобные конструкции, которые позволяют разработчику настраивать что-нибудь и разрывать в автоматическом режиме. Например, вам может потребоваться открыть файл, вписать в него кучу всего и закрыть. Это классический пример работы контекстного менеджера. Фактически, Python создает один такой экземпляр автоматически каждый раз, когда вы открываете файл, используя оператор with:
В Python 2.4, вам нужно делать это старомодным способом:
Это работает путем использования двух волшебных методов Python: __enter__ и __exit__. Давайте попробуем создать собственный контекстный менеджер, чтобы увидеть, как это работает на практике.
Создаем класс Context Manager
Вместо того, чтобы переписывать открытый метод Python, мы создадим контекстный менеджер, который создает связь с базой данных SQLite, и закрывает её по окончанию работы. Вот простой пример:
В данном коде мы создали класс, который берет путь к файлу базы данных SQLite Python. Метод __enter__ выполняется автоматически, он создает и возвращает объект связи базы данных. Теперь мы можем создать курсор для записи в базу данных или чтобы её запросить. Когда мы выходим из оператора with, метод __exit__ запускается, закрывая таким образом связь. Давайте попробуем создать контекстный менеджер при помощи другого метода.
Создание контекстного менеджера с использованием contextlib
В Python 2.5 добавили не только оператор with, но также модуль contextlib. Это позволяет нам создать контекстный менеджер, используя функцию модуля contextlib под названием contextmanager в качестве декоратора. Давайте попробуем создать контекстный менеджер который открывает и закрывает файл после проделанной в нем работе:
Здесь мы просто импортируем contextmanager из contextlib и декорируем нашу функцию file_open с ним. Это позволяет нам вызвать file_open используя оператор with. В нашей функции мы открываем файл, отдаем его, чтобы функция calling могла использовать его. После того, как оператор закончит, контроль возвращается обратно к функции file_open, которая продолжает следовать по коду за вызываемым оператором. Это приводит оператор finally к исполнению, благодаря которому и закрывается файл. Если возникла ошибка OSError во время работы с файлом, она будет выявлена и оператор finally закроет обработчик файлов несмотря на это.
contextlib.closing()
Модуль contextlib содержит несколько полезных утилит. Первая – это класс closing, который закроет объект по завершению определенного блока кода. В документации Python есть пример кода, похожий на следующий:
В целом, мы создаем закрывающую функцию, которая завернута в контекстный менеджер. Это эквивалент того, что делает класс closing. Но есть небольшая разница: вместо декоратора, мы можем использовать класс class в нашем операторе with. Давайте взглянем:
В данном примере мы открыли страницу URL, но обернули её в наш класс closing. Это приведет к закрытию дескриптора веб-страницы, сразу после выхода из блока кода оператора with.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Telegram Чат & Канал
Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
contextlib.suppress(*exceptions)
Еще один полезный инструмент — класс suppress, который был добавлен в Python 3.4. Идея в том, что данная утилита контекстного менеджера может подавлять любое количество исключений. Скажем, нам нужно проигнорировать исключение FileNotFoundError. Если прописать следующий контекстный менеджер, то это не сработает:
Как мы видим, этот контекстный менеджер не выполняет обработку данного исключения. Если вам нужно проигнорировать эту ошибку, лучше напишите следующий код:
Здесь мы импортируем suppress и передаем его исключению FileNotFoundError. Если вы запустите этот код, вы увидите, что ничего не происходит, так как файл не существует, но и ошибка не возникает. Обратите внимание на то, что этот контекстный менеджер является реентрабельным, но об этом позже.
contextlib.redirect_stdout / redirect_stderr
Библиотека contextlib содержит несколько замечательных инструментов для перенаправления stdout и stderr, которые появились в Python 3.4 и 3.5 соответственно. До того, как эти инструменты появились, и когда вам нужно перенаправить stdout, вам нужно сделать что-то на подобии этого:
С модулем contextlib вы можете сделать следующее:
В обоих примерах мы перенаправили stdout к файлу. Когда мы вызываем справку Python, вместо вывода в stdout, она сохраняется непосредственно в файле. Вы также можете перенаправить stdout в какой-нибудь буфер или текстовый инструмент управления из арсенала пользовательского интерфейса, вроде Tkinter или wxPython.
ExitStack
ExitStack – это контекстный менеджер, который позволит вам легко комбинировать другие контекстные менеджеры, а также функции очистки. Звучит немного запутанно, на первый взгляд, так что давайте рассмотрим простой пример из документации Python, с его помощью будет проще уловить суть:
В общем и целом, данный код создает серию контекстных менеджеров внутри списка. ExitStack поддерживает стек регистрируемых колбеков, которые вызываются в обратом порядке когда экземпляр закрыт, что и происходит, когда мы выходим из части the оператора with. В документации Python существует великое множество метких примеров работы contextlib, где вы можете ознакомиться с такими темами как:
Я настоятельно рекомендую ознакомиться с этими темами, так как вы поймете, насколько эффективным и полезным может быть этот класс.
Реентерабельные контекстные менеджеры
Большая часть создаваемых вами контекстных менеджеров может быть написана только для использования с оператором with для одноразового применения. Вот пример:
Здесь мы создали экземпляр контекстного менеджера и пытаемся запустить его дважды с оператором with. Второй запуск приводит к ошибке RuntimeError. Но что делать, если нам необходимо, чтобы контекстный менеджер запускался дважды? Для этой цели нам и нужен реентрабельный контекстный менеджер. Давайте используем менеджер redirect_stdout, который мы применяли ранее.
Здесь мы создали вложенные контекстные менеджеры, которые оба пишут в StringIO, который является текстовым потоком в памяти. Причина, по которой это работает, а не приводит к ошибке RuntimeError, как было ранее в том, что redirect_stdout является реентрабельным и позволяет нам вызывать его дважды. Конечно, ситуации в реальной жизни могут быть заметно сложнее, когда мы работаем с большим количеством функций, которые вызывают друг друга. Пожалуйста, обратите также внимание на то, что контекстные менеджеры не обязательно являются защищенными от потоков. Обратитесь к документации, перед тем как использовать их в потоках во избежание путаницы.
Подведем итоги
Контекстный менеджер – крайне полезный и удобный инструмент, способный выручить во многих ситуациях. Я пользуюсь ими в своих автоматических тестах постоянно, для открытия и закрытия диалогов, например. Теперь вы можете использовать ряд встроенных в Python инструментов для создания собственных контекстных менеджеров. Убедитесь в том, что вы выделили достаточно времени для изучения документации Python о contextlib, так как в ней хранится очень много дополнительной полезной информации, которая не была рассмотрена в данной статье.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
Использование менеджера контекста в Python
В этой статье вы узнаете что такое контекстный менеджер, и как он упрощает работу в Python.
Введение
Контекстный менеджер помогает упростить некоторые общие шаблоны управления ресурсами, абстрагируя их функциональность и позволяя их учитывать и повторно использовать. Давайте разберемся в упрощенном варианте.
Причины использования
Программисты время от времени работают с внешними ресурсами, такими как файлы, соединения с базами данных, блокировки и так далее. Контекстные менеджеры позволяют нам управлять этими ресурсами, указывая:
Рассмотрим следующий пример:
Обратите внимание, что я вызываю метод close(), чтобы гарантировать, что файловый дескриптор освобождается каждый раз. Если бы я этого не сделал, наша ОС (операционная система) в конечном итоге исчерпала бы свой разрешенный лимит на открытие файловых дескрипторов.
Однако я напишу более удобочитаемый вариант с помощью контекстного менеджера:
В этом примере open(«temp.txt», «+a») является менеджером контекста, который активируется с помощью оператора with. Обратите внимание, что мне не нужно было явно закрывать документ, контекстный менеджер позаботился об этом за меня. Точно так же в Python есть и другие предопределенные контекстные менеджеры, которые облегчают нашу работу.
Создание менеджера контекста
Существует два способа определения пользовательского контекстного менеджера:
Контекстный менеджер на основе класса
Давайте продолжим с нашим примером и попробуем определить наш собственный контекстный менеджер, который будет эмулировать функцию open():
Обработка ошибок
Пример того как я обрабатываю FileNotFoundError:
Это базовый код обработки ошибок, который должен быть каждый раз, когда вы открываете файл. Давайте попробуем добавить его в наш созданный менеджер контекста:
Изменения в атрибутах метода _exit_:
Контекстные менеджеры на основе функции
Управление контекстом на основе функций осуществляется с помощью библиотеки под названием contextlib, с помощью которого мы можем превратить простую функцию-генератор в контекстный менеджер. Вот как выглядит типичный код:
Обработка файлов
Я заключаю yield в блок try, потому что не знаю, что пользователь собирается делать с объектом open_file.
Заключение
Мы только что рассмотрели введение в контекстные менеджеры, но я чувствую, что это только верхушка айсберга, и для них есть много интересных вариантов использования.



