2.24. Контекстні менеджери¶
Оператор with виконує код ініціалізації, потім тіло, а потім код завершення – з гарантією, що код завершення виконається навіть якщо тіло завершиться з помилкою. Пара методів, що забезпечує ініціалізацію та завершення, називається контекстним менеджером.
Синтаксис:
with <expression> as <name>:
<body>
Вираз повертає контекстний менеджер. Python викликає його метод __enter__, опціонально прив’язує результат до <name>, виконує тіло, а потім викликає __exit__ – незалежно від того, чи тіло завершилося нормально або викинуло виключення.
__exit__ виконується в кінці блоку незалежно від того, що відбувається всередині нього.¶
2.24.1. Використання контекстного менеджера¶
Класичний приклад — відкриття файлу:
with open("data.txt") as f:
text = f.read()
# f is now closed, even if read() failed
open() повертає об’єкт файлу, який сам є контекстним менеджером; __enter__ повертає файл, а __exit__ закриває його. Блок with робить принцип «завжди закривати файл після роботи» типовою поведінкою, а не тим, про що має пам’ятати той, хто викликає.
2.24.1.1. Кілька контекстних менеджерів¶
Один оператор with може одночасно входити до кількох контекстних менеджерів, розділених комами:
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())
Еквівалентно двом вкладеним блокам with, але плоскіше. Менеджери входять зліва направо і виходять у зворотному порядку; якщо __enter__ правого менеджера викидає виключення, __exit__ лівого менеджера все одно виконається.
2.24.2. Написання контекстного менеджера¶
Будь-який клас з методами __enter__ і __exit__ може використовуватися як контекстний менеджер:
class Section:
def __init__(self, label):
self.label = label
def __enter__(self):
print("---", self.label, "---")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("--- end", self.label, "---")
return False
with Section("setup"):
print("doing the work")
Виведення:
--- setup ---
doing the work
--- end setup ---
__exit__ отримує три аргументи, що описують виключення, яке завершило блок, або три значення None, якщо блок завершився нормально. Повернення False (або None) дозволяє будь-якому виключенню поширюватися після завершення; повернення True поглинуло б його.
Використовуйте контекстні менеджери для будь-якого ресурсу, що має цикл «відкрити / закрити» або «захопити / звільнити» – не лише для файлів. Цей шаблон тримає завершення поруч з ініціалізацією в тому місці, де обидва представлені, тому забутий виклик close в середині довгої функції не може призвести до витоку ресурсу.