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 означает, что контекстный менеджер не подавляет исключения. Это правильное поведение для файлового менеджера: файл закрывается, но ошибка не скрывается.