Управление ресурсами является жизненно важным аспектом программного обеспечения. Закрытие открытых файлов, освобождение динамической памяти, завершение сеансов базы данных. Хотя это утомительно, мы знаем, что это необходимо.
Но разве не было бы неплохо, если бы мы могли делегировать распределение и освобождение ресурсов, избегая этих громоздких Попробуйте ... наконец
блоки?
Что ж, у Python наши спины благодаря с
Заявления и контекстных менеджеров!
А с заявлением
Многие из вас будут знакомы с «Pythonic» способом открытия файла, работы с ним, а затем следить за тем, чтобы он был закрыт; что было бы чем -то вроде:
with open('my-file.txt') as my_file: # Do your thing
Таким образом, вы открываете файл, работаете с ним и знаете, что он будет автоматически закрыт, как только вы закончите; Даже если ваш код повышает ошибку. Этот код эквивалентен:
my_file = open('my-file.txt') try: # Do your thing finally: my_file.close()
Однако, как вы можете видеть; Первое решение намного чище и легче понять. Эта версия является чистой бизнес -логикой, вы открываете файл и делаете что -то с ним. Он не включает в себя тот факт, что файл должен быть закрыт или что ошибка может возникнуть при работе с файлом, как вторая версия, все это сделано за кулисами.
Другой экземпляр, когда вы можете использовать с
оператор используется при использовании объекта блокировки из потокового модуля.
lock = Lock() # Why write this? lock.acquire() try: ... finally: lock.release() # When you can write this with lock: ...
Теперь вопрос в том, можете ли вы управлять своими пользовательскими ресурсами, используя с
утверждение?
Да! Ты сможешь!
Контекстные менеджеры
Контекст -диспетчер – это любой объект, который указывает две операции, которые следует выполнять одну за другой, с некоторой произвольной логикой между ними. Это происходит через методы __enter __ ()
и __exit __ ()
Анкет Обратите внимание, что мы говорим о любых двух операциях, не обязательно приобретении и выпуске ресурса.
Эти контекстные менеджеры могут быть использованы внутри с
утверждение следующим образом:
with some_context_manager [as foo]: # Your code
Когда Python выполняет этот кусок кода:
some_context_manager .__ Enter __ ()
называется. Метод передается без аргументов и, есликак
Предложение использовалось, его возвращаемое значение связано с именемФу
Внутри внутреннего блока.Ваш код выполняется, имея доступ к
foo
Если указано.some_context_manager .__ Выход __ ()
называется. Это передается 3 аргумента для обработки любого исключения, поднятого вашим кодом. Если не было никаких исключений, все триНет
Анкет Эти:exc_type
: Тип поднятого исключения.exc
: само исключение.Exc_traceback
: Traceback исключения.
Если
__exit __ ()
Желания подавить исключение, поднятое внутренним кодовым блоком, предотвращая его размножение, оно должно вернутьВерно
Анкет
Написание собственного контекстного менеджера
Как мы описали ранее, менеджер контекста справедливо и объект, который отвечает на __enter __ ()
и __exit __ ()
методы Мы можем написать простого менеджера, как это:
class MyContextManager: def __enter__(self): print('Acquiring a resource') self.resource = acquire() return self.resource def __exit__(self, exc_type, exc, exc_traceback): if exc_type is not None: print('Oops, this error was raised:', exc) print('Releasing the resource') self.resource.release()
Попробуем это.
>>> with MyContextManager(): ... print('Using the resource') # Output Acquiring a resource Using the resource Releasing the resource
Теперь давайте посмотрим, что произойдет, если мы подведем исключение внутри с
утверждение:
>>> with MyContextManager(): ... raise ValueError('Something went wrong') # Output Acquiring a resource Oops, this error was raised: Something went wrong Releasing the resource Traceback (most recent call last): File "", line 2, in ValueError: Something went wrong
Вы можете видеть это, хотя мы выразили ошибку внутри кодового блока, Выход () Метод все еще был выполнен, и ресурс был выпущен до пропаганды исключения за пределами с
утверждение.
Что если мы хотим подавить ошибку? В этом случае мы могли бы изменить __exit __ ()
и вернуть Верно
Из внедрения:
def __exit__(self, exc_type, exc, exc_traceback): if exc_type is not None: print('Oops, this error was raised:', exc) print('Releasing the resource') self.resource.release() return True
И сейчас:
>>> with MyContextManager(): ... raise ValueError('Something went wrong') # Output Acquiring a resource Oops, this error was raised: Something went wrong Releasing the resource
Исключение было поднято и __exit __ ()
Может сделать что -то, чтобы справиться с этим, но это не было распространено.
Написание контекстного менеджера в качестве функции
Встроенный модуль Python контекст Предоставляет утилиты для работы с контекстными менеджерами. Он включает в себя много полезных вещей, таких как общие контекстные менеджеры, уже реализованные, но в этом разделе мы сосредоточимся на
ContextManager декоратор.
Этот декоратор позволяет писать диспетчер контекста в качестве функции, которая возвращает генератор, который дает одно значение. Например, мы могли бы написать MyContextManager
как это:
from contextlib import contextmanager @contextmanager def some_resource(): print('Acquiring a resource') resource = acquire() try: yield resource except Exception as exc: print('Oops, this error was raised:', exc) raise finally: print('Releasing the resource') resource.release()
В этом случае, когда вы бежите:
with some_resource() [as foo]: # Your code
some_resource ()
выполняется додоход
утверждениеВаш код выполняется, имея ресурс, связанный с именем
foo
Если указано.Остальная часть
some_resource ()
выполнено.
Я лично предпочитаю этот метод для написания простых менеджеров. Для более сложных, как правило, лучше использовать класс.
Пример реального мира
Недавно я был в сценарии автоматизации Python, который работал на экземпляре Amazon EC2, и в какой -то момент он должен выполнить awscli
Команда с использованием другой учетной записи AWS, чем у виртуальной машины. Одним из способов временного использования другой учетной записи является получение учетных данных этой учетной записи и хранение их в двух переменных среды, AWS_ACCESS_KEY_ID
и AWS_SECRET_ACCESS_KEY
.
Так; Мне нужно было установить эти переменные среды, запустить команду CLI и отложить переменные, чтобы я мог продолжать использовать свою исходную учетную запись. Звучит идеально для менеджера контекста!
from os import environ, unsetenv from contextlib import contextmanager @contextmanager def aws_credentials(access_key: str, secret_key: str): environ['AWS_ACCESS_KEY_ID'] = access_key environ['AWS_SECRET_ACCESS_KEY'] = secret_key try: yield finally: unsetenv('AWS_ACCESS_KEY_ID') unsetenv('AWS_SECRET_ACCESS_KEY')
Обратите внимание, что использование урожай само по себе эквивалентно написанию не допустить .
Как вы видете; Этот менеджер контекста устанавливает учетные данные среды, дает так, чтобы код внутри с
Заявление выполняется, а затем выявляет учетные данные. Его можно использовать следующим образом:
access_key, secret_key = securely_get_credentials() with aws_credentials(access_key, secret_key): # Run cli command
Вывод
Контекстные менеджеры – это очень простые инструменты, которые позволяют вам писать код, который намного более чистый и более краткий. И теперь, когда вы их знаете, вы будете удивлены тем, как часто они появляются!
Это был мой первый пост на Dev.to, надеюсь, вам понравилось. Если у вас есть какие -либо отзывы, не стесняйтесь сообщить мне 😄.
Ваше здоровье!
Оригинал: “https://dev.to/pablopaglilla/clean-resource-management-in-python-using-context-managers-36a2”