Как сделать свой контекст менеджер python
Контекстные менеджеры в Python
Управление ресурсами
В программах часто используются такие ресурсы, как файлы или базы данных. Но эти ресурсы ограничены в доступе. Поэтому основная проблема заключается в том, чтобы освободить их после использования. Если они не будут освобождены, это приведет к утечке ресурсов и может вызвать замедление работы системы или ее сбой. Было бы хорошо, если бы у пользователя был механизм для автоматического захвата и освобождения ресурсов. В Python это может быть достигнуто с помощью контекстных менеджеров, которые облегчают правильную обработку ресурсов.
Для работы с файлами часто используется конструкция, показанная в примере ниже. Она позволяет захватить ресурс, выполнить необходимые операции, а затем освободить его:
Давайте возьмем пример управления файлами. Когда файл открывается, используется дескриптор файла, который является ограниченным ресурсом. Только определенное количество файлов может быть открыто процессом одновременно. Следующая программа демонстрирует это.
Мы получили сообщение об ошибке, указывающее, что открыто слишком много файлов. Приведенный выше пример представляет собой случай утечки файлового дескриптора. Это происходит потому, что слишком много открытых файлов и они не закрыты. Бывает, что программист просто забывает закрыть открытый файл.
Управление ресурсами с помощью контекстного менеджера
Создание контекстного менеджера
Управление файлами с помощью контекстного менеджера
Давайте используем вышеприведенную концепцию для создания класса, который применяется для управлении файловыми ресурсами. Класс FileManager помогает открыть файл, записать/прочитать содержимое, а затем закрыть его.
Управление файлами с помощью контекстного менеджера и конструкции with
При выполнении блока with последовательно выполняются следующие операции:
Управление подключением к базе данных с помощью контекстного менеджера
Давайте создадим простую систему управления подключениями к базе данных. Количество подключений к базе данных, которые могут быть открыты одновременно, также ограничено (как и файловые дескрипторы). Поэтому контекстные менеджеры полезны при управлении соединениями с базой данных, так как есть вероятность, что программист может забыть закрыть соединение.
Управление подключениями к базе данных с помощью контекстного менеджера и оператора with
При выполнении блока with последовательно выполняются следующие операции:
Контекстные Менеджеры в 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, модуль Contextlib и Python с заявлением в этой статье от Mike Driscoll
Python вышел с особым новым ключевым словом несколько лет назад в Python 2.5, который известен как с утверждение. Это новое ключевое слово позволяет разработчику создавать контекстные менеджеры. Но ждать! Что такое контекстный менеджер? Они удобные конструкции, которые позволяют вам что-то устанавливать и разорвать что-то автоматически. Например, вы можете открыть файл, написать кучу материал к нему, а затем закрыть его. Это, вероятно, классический пример контекстно-менеджера. На самом деле, Python автоматически создает один для вас, когда вы открываете файл, используя с утверждение:
Вернуться в Python 2.4, вам придется сделать это старомодный путь:
То, как это работает под крышками, с использованием некоторых магических методов Python: __enter__ и __exit__ Отказ Попробуем создать свой собственный менеджер контекста, чтобы продемонстрировать, как это все работает!
Создание класса менеджера контекста
Вместо того, чтобы переписать открытый метод Python здесь, вы создадите менеджер контекста, который может создать подключение к базе данных SQLite и закрыть его, когда это сделано. Вот простой пример:
В вышеупомянутом коде вы создали класс, который передает путь к файлу базы данных SQLite. __enter__ Способ выполняется автоматически, где он создает и возвращает объект подключения к базе данных. Теперь, когда у вас есть, вы можете создать курсор и записывать в базу данных или запросить его. Когда вы выходите из с Заявление, это вызывает __exit__ Способ выполнить и это закрывает соединение.
Попробуем создать менеджер контекста, используя другой метод.
Создание менеджера контекста с использованием ContextLib
Python 2.5 не только добавлен с заявление, но он также добавил Contextlib модуль. Это позволяет создать менеджер контекста, используя Contextlib ContextManager Функция как декоратор.
Давайте попробуем создать контекстный менеджер, который открывается и закрывает файл в конце концов:
Здесь вы импортируете ContextManager от Contextlib и украсить ваш file_open () функционировать с этим. Это позволяет звонить file_open () Использование Python’s с утверждение. В вашей функции вы открываете файл, а затем доходность Это так, чтобы вызывающая функция может использовать ее.
Однажды с Заявление о заявлении, контроль возвращается обратно в file_open () и он продолжается с кодом, следуя за доходность утверждение. Это вызывает Наконец Заявление для выполнения, который закрывает файл. Если у вас есть Осэррор Работая с файлом, он пойман, а Наконец Заявление все еще закрывает обработчик файла.
contextlib.closing ()
Модуль Contextlib поставляется с другими удобными утилитами. Первый – это закрытие Класс, который закроет вещь на завершение блока кода. Документация Python дает пример, который аналогичен следующему:
В основном то, что вы делаете, является создание функции закрытия, которая завернута в ContextManager Отказ Это эквивалент того, что делает класс закрытия. Разница в том, что вместо декоратора вы можете использовать закрытие Сам класс в вашем с заявлением.
Вот что это будет выглядеть:
В этом примере вы открываете URL, но оберните его своим классом закрытия. Это приведет к тому, что ручка на веб-странице будет закрыта, как только вы выпадете из с Заявление о кодовом блоке.
contextlib.suppress (* исключения)
Еще один удобный маленький инструмент – подавить Класс, который был добавлен в Python 3.4. Идея этой утилиты контекстно-менеджера заключается в том, что он может подавлять любое количество исключений. Общий пример, когда вы хотите игнорировать FileNotfoundError исключение. Если вы должны были написать следующий менеджер контекста, он не будет работать:
Этот менеджер контекста не обрабатывает это исключение. Если вы хотите игнорировать эту ошибку, вы можете сделать следующее:
Здесь вы импортируете подавить и пропустите его исключение, которое вы хотите игнорировать, что в этом случае является FileNotfoundError исключение. Если вы запустите этот код, ничего не происходит, так как файл не существует, но ошибка также не поднимается. Следует отметить, что этот контекстный менеджер является ReentRant Отказ Это будет объяснено позже в этой статье.
contextlib.redirect_stdout/redirect_stderr.
Библиотека Contextlib имеет пару аккуратных инструментов для перенаправления Stdout и Stderr, которые были добавлены в Python 3.4 и 3.5 соответственно. Прежде чем эти инструменты были добавлены, если вы хотите перенаправить Stdout, вы сделаете что-то подобное:
С Contextlib Модуль, теперь вы можете сделать следующее:
В обоих этих примерах вы перенаправляете stdout к файлу. Когда вы называете Python’s Помощь () вместо печати на stdout он сохраняется непосредственно в файл. Вы также можете перенаправить stdout к какому виду буфера или виджета типа управления текстовым элементом от инструментария пользовательского интерфейса, такого как TKinter или WxPython.
ExitStack.
ExitStack Является ли менеджер контекста, который позволит вам легко программно объединить другие контекстные менеджеры и функции очистки. Сначала звучит вид с толку, поэтому давайте посмотрим на примере документации Python, чтобы помочь вам понять эту идею немного лучше:
Этот код в основном создает ряд контекстных менеджеров внутри понимания списка. ExitStack Поддерживает стопку зарегистрированных обратных вызовов, которые оно позвонит в обратном порядке, когда экземпляр закрыт, что происходит при выходе из нижней части с утверждение.
В документации Python есть куча аккуратных примеров для Contextlib Где вы можете узнать о темах, таких как следующее:
Вы должны проверить это, чтобы вы получили хорошее почувствовать, насколько мощный этот класс.
Обертывание
Контекстные менеджеры очень веселые и все время пригодятся. Я использую их в моих автоматизированных тестах все время для открытия и закрытия диалогов. Теперь вы должны быть в состоянии использовать встроенные инструменты Python для создания собственных контекстных менеджеров. Обязательно найдите время, чтобы прочитать документацию Python в ContextLib, поскольку есть много дополнительной информации, которая не распространяется в этой главе. Весело и счастливое кодирование!
Контекстные менеджеры в 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 можно использовать в самых разных случаях. Начните использовать их сразу же, когда заметите шаблон «настройка-завершение», чтобы сделать свой код более питоничным.
Тип contextmanager, контекстный менеджер
Оператор with в Python поддерживает концепцию контекста среды выполнения, определенного контекстным менеджером. Типичные области применения контекстных менеджеров включают сохранение и восстановление различных типов глобального состояния, блокировку и разблокировку ресурсов, закрытие открытых файлов и т. д.
Содержание:
Синтаксис оператора контекста with :
Как работает менеджер контекста with :
Если последовательность команд была завершена по любой причине, кроме исключения, то возвращаемое значение из __exit__() игнорируется, и выполнение продолжается.
При наличии нескольких контекстных менеджеров, то они обрабатываются так, как если бы несколько операторов with были вложенными:
С версии Python 3.10 поддерживается использование круглых скобок для написания нескольких диспетчеров. Это позволяет форматировать длинную коллекцию диспетчеров контекста в несколько строк аналогично тому, как это можно с операторами импорта. Например, теперь действительны все эти примеры:
Допускается использовать конечную запятую в конце заключенной группы:
Реализация/протокол менеджера контекста.
Протокол контекстных менеджеров реализован с помощью пары методов, которые позволяют определяемым пользователем классам определять контекст среды выполнения, который вводится до выполнения тела инструкции и завершается при завершении инструкции:
contextmanager.__enter__() :
contextmanager.__exit__(exc_type, exc_val, exc_tb) :
Упрощенное создание менеджеров контекста.
Многие контекстные менеджеры, например, файлы и контексты на основе генераторов будут одноразовыми объектами. После вызова метода __exit__() менеджер контекста больше не будет находиться в работоспособном состоянии (например, файл был закрыт или базовый генератор завершил выполнение).