Менеджери контекста
Менеджер контекста (context manager) – об'єкт, який створює контекст виконання всередині оператора
with
і реалізує два методи:_enter_
та_exit_
.
Менеджери контекста використовуються для збереження і відновлення глобального стану, блокування і розблокування ресурсів, автоматичного закрыття файлів та інше.
Припустимо, у вас є дві пов'язані операції, які ви хочете виконати у парі, помістивши між ними блок кода. Менеджери контекста дозволяють зробити саме це. Наприклад:
with open('some_file', 'w') as opened_file:
opened_file.write('Hello!')
Цей код відкриває файл, записує в нього дані і закриває після цього файл. При виникненні помилки при запису даних в файл менеджер контекста спробує його закрити. Цей код є еквівалентним наступному:
file = open('some_file.txt', 'w')
try:
file.write('Hello!')
finally:
file.close()
Порівнявши з попереднім блоком кода можна зауважити заміну шаблоного кода на використання with
.
Синтаксис конструкції with ... as
виглядає так:
with <вираз> as <змінна>:
<блок коду>
Частина as ...
є необов'язковою.
Якщо в конструкції with ... as
було декілька виразів,
то це еквівалентно декільком вкладеним інструкциіям.
Наприклад:
with A() as a, B() as b:
# some code
еквівалентнло наступному:
with A() as a:
with B() as b:
# some code
Розглянемо як можна створити власний менеджер контекста.
Контекст-менеджер як клас
Щоб об'єкт став менеджером контекста він має реалізовувати два спеціальних метода:
__enter__()
__exit__()
Нагадаємо собі ще раз конструкцію with
:
with <вираз> as <змінна>:
<блок коду>
При виконанні даного блока відбувається наступне:
- Виконується вираз в конструкції
with ... as
. - Завантажується спеціальний метод
__exit__
для подальшого використання. - Виконується спеціальний метод
__enter__
. Якщо конструкціяwith
включає в себеas
, то значення, яке повертається методом__enter__
пов'язується зі вказаною змінною. - Виконується блок коду.
- Викликається метод
__exit__
, незалежно чи виконався блок коду чи під час його виконання сталась виняткова ситуація.
Метод __enter__()
має повертати ресурс, над яким виконуються дії в рамках даного контекста.
У методі __exit__()
можна обробити виняткові ситуації, які виникли під час виконання блока коду. Йому передаються три параметри:
- exception_class — клас винятка
- exception_value — екземпляр винятка
- traceback — інформація про виняток
Якщо під час виконання блока жодного винятка не було піднято, усім цим параметрам передається значення None
.
Якщо метод __exit__()
повертає True
— вважається,
що винятки оброблено у цьому методі і нагору вони не піднімаються.
Давайте спробуємо створити власний контекст-менеджер.
Припустимо нам треба виконати певні дії
помінявши перед цим поточну робочу директорію нашої програми у файловій системі,
а потім встановити поточну директорію такою, як була раніше.
Нам знадобляться дві функції з модуля os
:
os.chdir()
— встановлює поточну директоріюos.getcwd()
— повертає поточну директорію
Наш менеджер контекста може виглядати так:
>>> from os import chdir, getcwd
>>> class cd:
... def __init__(self, path):
... self.path = path
... def __enter__(self):
... self.old_path = getcwd()
... chdir(self.path)
... def __exit__(self, c, e, t):
... chdir(self.old_path)
... return False
...
>>>
Спробуємо скористатись щойно створеним менеджером контекста (у вас результат буде іншим):
>>> print(getcwd())
c:\\dev\with
>>> with cd("/"):
... print(getcwd())
...
c:\
>>> print(getcwd())
c:\\dev\with
>>>