В Python контекстные менеджеры, используемые с оператором with, обеспечивают элегантный и надежный способ управления ресурсами, гарантируя их корректное высвобождение, независимо от того, произошло ли исключение в блоке кода или нет.  Механизм закрытия ресурсов в контекстных менеджерах строится на использовании двух специальных методов:
__enter__(): Этот метод вызывается при входе в блок with.  Он может выполнять любую подготовительную работу, например, открывать файл, устанавливать соединение с базой данных или приобретать блокировку.  Он также может возвращать значение, которое присваивается переменной, указанной после as в операторе with (например, with open('file.txt') as f:).
    __exit__(exc_type, exc_val, exc_tb):  Этот метод вызывается при выходе из блока with, независимо от того, было ли вызвано исключение или нет.  Он принимает три аргумента:
      exc_type: Тип исключения, если оно произошло в блоке with. Если исключения не было, то None.exc_val: Объект исключения, если оно произошло. Если исключения не было, то None.exc_tb: Объект трассировки стека, если исключение произошло. Если исключения не было, то None.Основная задача метода __exit__() – выполнить завершающую работу, такую как закрытие файла, разрыв соединения с базой данных или снятие блокировки.  Важно отметить, что __exit__() вызывается всегда, даже если в блоке with произошло необработанное исключение.
Возвращаемое значение метода __exit__() влияет на обработку исключения:
__exit__() возвращает True (или любое истинное значение), это сигнализирует о том, что исключение было обработано контекстным менеджером, и оно не будет распространяться дальше.  Программа продолжит выполнение после блока with.__exit__() возвращает False (или None, что является поведением по умолчанию), это сигнализирует о том, что исключение не было обработано, и оно будет переброшено дальше по стеку вызовов.Пример:
class ManagedFile:
    def __init__(self, filename):
      self.filename = filename
      self.file = None
    def __enter__(self):
      self.file = open(self.filename, 'w')
      return self.file
    def __exit__(self, exc_type, exc_val, exc_tb):
      if self.file:
        self.file.close()
      if exc_type:
        print(f"Исключение: {exc_type}, {exc_val}") # Обработка исключения (например, логирование)
      return True # Предотвращаем переброс исключения дальше
  with ManagedFile('notes.txt') as f:
    f.write('Some todo...')
    f.write('And another one')
    raise Exception('This is a test exception') # Вызываем исключение внутри блока with
  print("Код продолжает выполняться после with, даже после исключения.")
  В этом примере, даже если внутри блока with произойдет исключение, файл notes.txt все равно будет закрыт благодаря методу __exit__().  Кроме того, метод __exit__() обрабатывает исключение (выводя сообщение) и возвращает True, предотвращая его распространение. Это демонстрирует, как контекстные менеджеры гарантируют корректное высвобождение ресурсов и дают возможность контролировать обработку исключений.
Оператор with с контекстными менеджерами значительно упрощает код и делает его более надежным при работе с ресурсами, требующими явного освобождения.