EMZETT.
Увійти

Менеджери контексту і with: безпечне відкриття й закриття ресурсів

Коротко: Оператор with гарантує, що ресурси на кшталт файлів, з’єднань чи блокувань звільняються навіть за помилок; власні менеджери контексту ви створюєте за допомогою класів або contextlib.

Докладніше: Хто відкрив файл, має його закрити, інакше дескриптори лишаються відкритими, дані незаписаними, блокування утримуваними. try/finally працює, але незручний. with робить це автоматично.

Подробиці

Шаблон

with open("data.txt", encoding="utf-8") as f:
    content = f.read()
# тут файл гарантовано закритий, навіть за винятку

Еквівалентно try: ... finally: f.close(). Кілька ресурсів: with open(a) as x, open(b, "w") as y:. Типові менеджери контексту: файли, threading.Lock(), з’єднання/транзакції бази даних (with conn:), tempfile.TemporaryDirectory(), socket, zipfile, unittest.mock.patch, subprocess, HTTP-сеанси (requests.Session).

Власні менеджери контексту

Клас з __enter__ та __exit__:

import time
class Stopwatch:
    def __enter__(self):
        self.start = time.perf_counter(); return self
    def __exit__(self, typ, value, tb):
        print(f"{time.perf_counter() - self.start:.3f}s")
        return False        # не ковтати виняток

Генератор з contextlib:

from contextlib import contextmanager
@contextmanager
def workdir(path):
    old = os.getcwd(); os.chdir(path)
    try:
        yield
    finally:
        os.chdir(old)

__exit__ отримує відомості про виняток; повернення True пригнічує його (рідко розумно; contextlib.suppress(FileNotFoundError) — безпечний варіант).

Інші інструменти

  • contextlib.closing, ExitStack (динамічно багато ресурсів), redirect_stdout.
  • async: async with для асинхронних ресурсів (пул бази даних, HTTP-клієнт aiohttp), __aenter__/__aexit__.
  • Файли: Path.read_text() обгортає with для однорядкових рішень.
  • Транзакції: with db.transaction(): автоматично commit/rollback.
  • Тести: with pytest.raises(ValueError):, with patch(...).
  • Блокування: with lock: запобігає забутому release() (див. mutex/deadlock).

Типові помилки та поради

  • Залишити файл відкритим без with (Windows тоді не зможе його видалити).
  • Повернення з блоку with допустиме: __exit__ усе одно виконується.
  • Генераторний менеджер контексту без try/finally не очищає за винятку.
  • Не ковтайте винятки: __exit__ повертає False/None.
  • Завжди вказуйте encoding під час відкриття тексту.
  • Витоки ресурсів виявляються через ResourceWarning/-X dev.
  • Пов’язане: RAII (C++), try-with-resources (Java), defer (Go), using (C#).

Див. також: Файли в Python, М’ютекс