что такое контекстный менеджер в python

Использование менеджера контекста в Python

В этой статье вы узнаете что такое контекстный менеджер, и как он упрощает работу в Python.

Введение

Контекстный менеджер помогает упростить некоторые общие шаблоны управления ресурсами, абстрагируя их функциональность и позволяя их учитывать и повторно использовать. Давайте разберемся в упрощенном варианте.

Причины использования

Программисты время от времени работают с внешними ресурсами, такими как файлы, соединения с базами данных, блокировки и так далее. Контекстные менеджеры позволяют нам управлять этими ресурсами, указывая:

Рассмотрим следующий пример:

Обратите внимание, что я вызываю метод close(), чтобы гарантировать, что файловый дескриптор освобождается каждый раз. Если бы я этого не сделал, наша ОС (операционная система) в конечном итоге исчерпала бы свой разрешенный лимит на открытие файловых дескрипторов.

Однако я напишу более удобочитаемый вариант с помощью контекстного менеджера:

В этом примере open(«temp.txt», «+a») является менеджером контекста, который активируется с помощью оператора with. Обратите внимание, что мне не нужно было явно закрывать документ, контекстный менеджер позаботился об этом за меня. Точно так же в Python есть и другие предопределенные контекстные менеджеры, которые облегчают нашу работу.

Создание менеджера контекста

Существует два способа определения пользовательского контекстного менеджера:

Контекстный менеджер на основе класса

Давайте продолжим с нашим примером и попробуем определить наш собственный контекстный менеджер, который будет эмулировать функцию open():

Обработка ошибок

Пример того как я обрабатываю FileNotFoundError:

Это базовый код обработки ошибок, который должен быть каждый раз, когда вы открываете файл. Давайте попробуем добавить его в наш созданный менеджер контекста:

Изменения в атрибутах метода _exit_:

Контекстные менеджеры на основе функции

Управление контекстом на основе функций осуществляется с помощью библиотеки под названием contextlib, с помощью которого мы можем превратить простую функцию-генератор в контекстный менеджер. Вот как выглядит типичный код:

Обработка файлов

Я заключаю yield в блок try, потому что не знаю, что пользователь собирается делать с объектом open_file.

Заключение

Мы только что рассмотрели введение в контекстные менеджеры, но я чувствую, что это только верхушка айсберга, и для них есть много интересных вариантов использования.

Источник

Контекстные менеджеры в 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–“)

Источник

Некоторые возможности Python о которых вы возможно не знали

Предисловие

Я очень полюбил Python после того, как прочитал книгу Марка Лутца «Изучаем Python». Язык очень красив, на нем приятно писать и выражать собственные идеи. Большое количество интерпретаторов и компиляторов, расширений, модулей и фреймворков говорит о том, что сообщество очень активно и язык развивается. В процессе изучения языка у меня появилось много вопросов, которые я тщательно гуглил и старался понять каждую непонятую мной конструкцию. Об этом мы и поговорим с вами в этой статье, статья ориентирована на начинающего Python разработчика.

Немного о терминах

Начну пожалуй с терминов, которые часто путают начинающих Python программистов.

List comprehensions или генераторы списков возвращают список. Я всегда путал генераторы списков и выражения — генераторы (но не генераторы выражений!). Согласитесь, по русский звучит очень похоже. Выражения — генераторы это generator expressions, специальные выражения, которые возвращают итератор, а не список. Давайте сравним:

Это две совершенно разные конструкции. Первый возвращает генератор (то есть итератор), второй обычный список.

Generators или генераторы это специальные функции, которые возвращают итератор. Что бы получить генератор нужно возвратить функции значение через yield:

Кстати, в Python 3.3 появилась новая конструкция yield from. Совместное использование yield и for используется настолько часто, что эти две конструкции решили объединить.

Что такое контекстные менеджеры и для чего они нужны?

Контекстные менеджеры это специальные конструкции, которые представляют из себя блоки кода, заключенные в инструкцию with. Инструкция with создает блок используя протокол контекстного менеджера, о котором мы поговорим далее в этой статье. Простейшей функцией, использующей данный протокол является функция open(). Каждый раз, как мы открываем файл нам необходимо его закрыть, что бы вытолкнуть выходные данные на диск (на самом деле Python вызывает метод close() автоматически, но явное его использование является хорошим тоном). Например:

Что бы каждый раз не вызывать метод close() мы можем воспользоваться контекстным менеджером функции open(), который автоматически закроет файл после выхода из блока:

Здесь нам не нужно каждый раз вызывать метод close, что бы вытолкнуть данные в файл. Из этого следует, что контекстный менеджер используется для выполнения каких либо действий до входа в блок и после выхода из него. Но функциональность контекстных менеджеров на этом не заканчивается. Во многих языках программирования для подобных задач используются деструкторы. Но в Python если объект используется где то еще то нет гарантии, что деструктор будет вызван, так как метод __del__ вызывается только в том случае, если все ссылки на объект были исчерпаны:

Решим эту задачу через контекстные менеджеры:

Теперь попробуем вызвать менеджер контекста:

Мы увидели, что произошел гарантированный выход из блока после выполнения нашего кода.

Протокол контекстного менеджера

Мы уже кратко рассмотрели протокол контекстного менеджера написав небольшой класс Hello. Давайте теперь разберемся в протоколе более подробно. Что бы объект стал контекстным менеджером в его класс обязательно нужно включить два метода: __enter__ и __exit__. Первый метод выполняется до входа в блок. Методу можно возвратить текущий экземпляр класса, что бы к нему можно было обращаться через инструкцию as.

Метод __exit__ выполняется после выхода из блока with, и он содержит три параметра — exp_type, exp_value и exp_tr. Контекстный менеджер может вылавливать исключения, которые были возбуждены в блоке with. Мы можем вылавливать только нужные нам исключения или подавлять ненужные.

Переменная exp_type содержит в себе класс исключения, которое было возбуждено, exp_value — сообщение исключения. В примере мы закрываем файл и подавляем исключение IOError посредством возврата True методу __exit__. Все остальные исключения в блоке мы разрешаем. Как только наш код подходит к концу и блок заканчивается вызывается метод self.fp.close(), не зависимо от того, какое исключение было возбуждено. Кстати, внутри блока with можно подавлять и такие исключения как NameError, SyntaxError, но этого делать не стоит.

Протоколы контекстных менеджеров очень просты в использовании, но для обычных задач есть еще более простой способ, который поставляется вместе со стандартной библиотекой питона. Далее мы рассмотрим пакет contextlib.

Пакет contextlib

Создание контекстных менеджеров традиционным способом, то есть написанием классов с методами __enter__ и __exit__ не одна из сложных задач. Но для тривиального кода написание подобных классов требует больше возьни. Для этих целей был придуман декоратор contextmanager(), входящий в состав пакета contextlib. Используя декоратор contextmanager() мы можем из обычной функции сделать контекстный менеджер:

Проверим работоспособность кода:

Попробуем возбудить исключение внутри блока.

Как видно из примера, реализация с использованием классов практически ничем не отличается по функциональности от реализации с использованием декоратора contextmanager(), но использование декоратора намного упрощает наш код.

Еще один интересный пример использования декоратора contextmanager():

Похоже на блоки в руби не так ли?

И напоследок поговорим о вложенных контекстах. Вложенные контексты позволяют управлять несколькими контекстами одновременно. Например:

вход в контекст first
вход в контекст second
внутри блока first second
выход из контекста second
выход из контекста first

Аналогичный код без использования функции nested:

Этот код хоть и похож на предыдущий, в некоторых ситуациях он будет работать не так как нам хотелось бы. Объекты context(‘first’) и context(‘second’) вызываются до входа в блок, поэтому мы не сможем перехватывать исключения, которые были возбуждены в этих объектах. Согласитесь, первый вариант намного компактнее и выглядит красивее. А вот в Python 2.7 и 3.1 функция nested устарела и была добавлена новая синтаксическая конструкция для вложенных контекстов:

range и xrange в Python 2.7 и Python 3

Известно, что Python 2.7 range возвращает список. Думаю все согласятся, что хранить большие объемы данных в памяти нецелесообразно, поэтому мы используем функцию xrange, возвращающий объект xrange который ведет себя почти так же как и список, но не хранит в памяти все выдаваемые элементы. Но меня немного удивило поведение xrange в Python 2.x, когда функции передаются большие значения. Давайте посмотрим на пример:

Python нам говорит о том, что int слишком длинный и он не может быть переконвертирован в C long. Оказывается у Python 2.x есть ограничения на целое число, в этом мы можем убедиться просмотрев константу sys.maxsize:

Вот оно максимальное значение целого числа:

Python аккуратно переконвертировал наше число в long int. Не удивляйтесь, если xrange в Python 2.x будет вести себя иначе при больших значениях.

В Python 3.3 целое число может быть бесконечно большим, давайте проверим:

Конвертирование в long int не произошло. Вот еще пример:

Не очевидное поведение некоторых конструкций

Думаю все согласятся, что простота питона заключена не в легкости его изучении, а в простоте самого языка. Питон красив, гибок и на нем можно писать не только в объектно ориентированном стиле, но и в функциональном. Но о поведении некоторых конструкций, который на первый взгляд кажутся странными необходимо знать. Для начала рассмотрим первый пример.

Каков будет результат выполнения данной конструкции? Неподготовленный разработчик сообщит о результате: [[‘a’], [b’], [c’]]. Но на самом деле мы получаем:

Почему в каждом списке результат дублируется? Дело в том, что оператор умножения создает ссылки внутри нашего списка на один и тот же список. В этом легко убедиться немного дополнив наш пример:

В первом случае все нормально и ссылки на списки разные, а во втором примере мы ссылаемся на один и тот же объект. Из этого следует, что изменение в первом списке повлечет за собой изменение в последующих, так что будьте внимательны.

Второй пример уже рассматривался на хабре, но мне захотелось включить его в статью. Посмотрим на lambda — функцию, которую мы будет прогонять через цикл for, и помещать каждую функцию в словарь:

В пределах lambda функции переменная i замыкается и как бы создается экземпляр еще одной переменной i в блоке lambda — функции, которая является ссылкой на переменную i в цикле for. Каждый раз когда счетчик цикла for меняется, меняются и значения во всех lambda функциях, поэтому мы получаем значение i-1 во всех функциях. Исправить это легко, явно передав lambda функции в качестве первого параметра значение по умолчанию — переменную i:

Источник

Тип contextmanager, контекстный менеджер

Оператор with в Python поддерживает концепцию контекста среды выполнения, определенного контекстным менеджером. Типичные области применения контекстных менеджеров включают сохранение и восстановление различных типов глобального состояния, блокировку и разблокировку ресурсов, закрытие открытых файлов и т. д.

Содержание:

Синтаксис оператора контекста with :

Как работает менеджер контекста with :

Если последовательность команд была завершена по любой причине, кроме исключения, то возвращаемое значение из __exit__() игнорируется, и выполнение продолжается.

При наличии нескольких контекстных менеджеров, то они обрабатываются так, как если бы несколько операторов with были вложенными:

С версии Python 3.10 поддерживается использование круглых скобок для написания нескольких диспетчеров. Это позволяет форматировать длинную коллекцию диспетчеров контекста в несколько строк аналогично тому, как это можно с операторами импорта. Например, теперь действительны все эти примеры:

Допускается использовать конечную запятую в конце заключенной группы:

Реализация/протокол менеджера контекста.

Протокол контекстных менеджеров реализован с помощью пары методов, которые позволяют определяемым пользователем классам определять контекст среды выполнения, который вводится до выполнения тела инструкции и завершается при завершении инструкции:

contextmanager.__enter__() :

contextmanager.__exit__(exc_type, exc_val, exc_tb) :

Упрощенное создание менеджеров контекста.

Многие контекстные менеджеры, например, файлы и контексты на основе генераторов будут одноразовыми объектами. После вызова метода __exit__() менеджер контекста больше не будет находиться в работоспособном состоянии (например, файл был закрыт или базовый генератор завершил выполнение).

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *