Реализовать контекстный менеджер для работы с файлом

31. Реализовать контекстный менеджер для работы с файлом

Условие задачи Реализовать класс FileManager, который работает как контекстный менеджер для открытия файла. Класс должен поддерживать использование через конструкцию with. При входе в контекст должен открываться файл с переданными параметрами, а при выходе из контекста файл должен корректно закрываться.

class FileManager:
    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


with FileManager('example.txt', 'w') as f:
    f.write('Hello, World!')

Дополнительно Какие могут быть ошибки при закрытии файла?

Спойлеры к решению
Подсказки
  • Контекстный менеджер должен реализовать методы __enter__ и __exit__.
  • В __init__ нужно сохранить путь к файлу, режим открытия и дополнительные параметры.
  • В __enter__ файл открывается через open().
  • Метод __enter__ должен вернуть файловый объект, чтобы он попал в переменную после as.
  • В __exit__ файл нужно закрыть через .close().
  • Если из __exit__ вернуть False или ничего не вернуть, исключения внутри with не будут подавлены.
Решение
class FileManager:
    def __init__(self, file_name: str, mode: str, encoding: str | None = None):
        self.file_name = file_name
        self.mode = mode
        self.encoding = encoding
        self.file = None

    def __enter__(self):
        self.file = open(self.file_name, self.mode, encoding=self.encoding)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file is not None:
            self.file.close()

        return False

Пример использования:

with FileManager("example.txt", "w", encoding="utf-8") as f:
    f.write("Hello, World!")

Класс FileManager работает как контекстный менеджер.

При входе в блок with вызывается метод:

__enter__

Он открывает файл и возвращает файловый объект. Именно этот объект попадает в переменную f.

При выходе из блока with вызывается метод:

__exit__

Он закрывает файл через:

self.file.close()

Параметры:

exc_type
exc_val
exc_tb

нужны для информации об исключении, если оно произошло внутри блока with.

Например:

with FileManager("example.txt", "w", encoding="utf-8") as f:
    f.write("Hello, World!")
    raise ValueError("Ошибка")

Даже в этом случае __exit__ будет вызван, и файл будет закрыт.

Возможные ошибки при закрытии файла:

1. OSError — ошибка ввода-вывода при закрытии файла.
2. Ошибка сброса буфера на диск, например при нехватке места.
3. Ошибка файловой системы или устройства.
4. Ошибка при работе с сетевым или внешним хранилищем.

Более осторожная реализация:

class FileManager:
    def __init__(self, file_name: str, mode: str, encoding: str | None = None):
        self.file_name = file_name
        self.mode = mode
        self.encoding = encoding
        self.file = None

    def __enter__(self):
        self.file = open(self.file_name, self.mode, encoding=self.encoding)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            if self.file is not None:
                self.file.close()
        finally:
            self.file = None

        return False

return False означает, что контекстный менеджер не подавляет исключения. Это правильное поведение для файлового менеджера: файл закрывается, но ошибка не скрывается.