Контекстный менеджер для работы с файлом
Условие
Реализуйте контекстный менеджер для работы с файлом без использования встроенного open().
Используйте методы enter и exit.
Пример использования
with FileManager("test.txt", "w") as f:
f.write("Hello, World!")
Дополнительно
Реализуйте то же самое с помощью @contextmanager из contextlib.
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Контекстный Менеджер для Работы с Файлом
Контекстный менеджер — это объект, который определяет, что происходит в начале блока with (метод __enter__) и в конце (метод __exit__). Это гарантирует правильное управление ресурсами.
Подход 1: Класс с enter и exit
class FileManager:
def __init__(self, filename: str, mode: str = 'r'):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
print(f"Opening file: {self.filename}")
self.file = open(self.filename, self.mode)
return self.file # Это становится переменной после 'as'
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"Closing file: {self.filename}")
if self.file:
self.file.close()
# Возвращаем False, чтобы пробросить исключение дальше
return False
# Использование
with FileManager("test.txt", "w") as f:
f.write("Hello, World!")
Вывод:
Opening file: test.txt
Closing file: test.txt
Параметры exit
__exit__(self, exc_type, exc_val, exc_tb) получает три параметра:
class FileManager:
def __exit__(self, exc_type, exc_val, exc_tb):
# exc_type: класс исключения (None если исключения не было)
# exc_val: значение исключения (сам объект ошибки)
# exc_tb: traceback исключения
if self.file:
self.file.close()
# Возвращаемое значение:
# True → подавить исключение (не пробрасывать дальше)
# False → пробросить исключение дальше (по умолчанию)
if exc_type is not None:
print(f"Exception occurred: {exc_type.__name__}: {exc_val}")
return False # Пробросить исключение
Обработка Исключений
class FileManager:
def __init__(self, filename: str, mode: str = 'r'):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
try:
self.file = open(self.filename, self.mode)
return self.file
except IOError as e:
print(f"Failed to open file: {e}")
raise
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
print(f"Error in context: {exc_type.__name__}: {exc_val}")
if self.file and not self.file.closed:
self.file.close()
return False # Пробросить исключение
# Использование с ошибкой
try:
with FileManager("test.txt", "w") as f:
f.write("test")
raise ValueError("Intentional error")
except ValueError as e:
print(f"Caught: {e}")
Подход 2: Декоратор @contextmanager
@contextmanager из модуля contextlib позволяет создавать менеджеры через генератор:
from contextlib import contextmanager
@contextmanager
def file_manager(filename: str, mode: str = 'r'):
print(f"Opening file: {filename}")
file = open(filename, mode)
try:
yield file # Это становится переменной после 'as'
finally:
print(f"Closing file: {filename}")
file.close()
# Использование
with file_manager("test.txt", "w") as f:
f.write("Hello, World!")
Структура @contextmanager:
@contextmanager
def my_manager():
# Код перед блоком with (__enter__)
resource = acquire_resource()
try:
yield resource # Результат становится переменной в 'as'
finally:
# Код после блока with (__exit__)
release_resource(resource)
Сравнение Подходов
# Подход 1: Класс (явный, более гибкий)
class MyContext:
def __enter__(self):
print("Enter")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exit")
return False
with MyContext() as ctx:
print("Inside")
# Подход 2: Генератор (компактный, удобный)
from contextlib import contextmanager
@contextmanager
def my_context():
print("Enter")
try:
yield None
finally:
print("Exit")
with my_context() as ctx:
print("Inside")
Практические Примеры
1. Таймер с контекстным менеджером:
import time
from contextlib import contextmanager
@contextmanager
def timer(name: str):
start = time.time()
print(f"Starting: {name}")
try:
yield
finally:
elapsed = time.time() - start
print(f"Finished: {name} ({elapsed:.2f}s)")
with timer("slow operation"):
time.sleep(1)
2. Временное изменение настроек:
from contextlib import contextmanager
@contextmanager
def temp_settings(debug=True):
old_debug = globals().get('DEBUG')
globals()['DEBUG'] = debug
try:
yield
finally:
if old_debug is None:
globals().pop('DEBUG', None)
else:
globals()['DEBUG'] = old_debug
3. Блокировка ресурса:
from contextlib import contextmanager
import threading
lock = threading.Lock()
@contextmanager
def acquire_lock():
lock.acquire()
print("Lock acquired")
try:
yield
finally:
lock.release()
print("Lock released")
with acquire_lock():
print("Critical section")
Встроенные Контекстные Менеджеры
Python имеет много встроенных контекстных менеджеров:
# Файлы
with open('file.txt') as f:
content = f.read()
# Блокировки
import threading
lock = threading.Lock()
with lock:
# критическая секция
pass
# Временное подавление исключений
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('file.txt') # Не упадёт, если файла нет
# Redirect вывода
from contextlib import redirect_stdout
with redirect_stdout(file):
print("Goes to file") # Вместо stdout
Рекомендация
Для собеседования:
- Класс с enter/exit — если нужен полный контроль
- @contextmanager — если логика простая и используется try/finally
Когда использовать контекстные менеджеры:
- Открытие/закрытие файлов
- Захват/освобождение блокировок
- Соединения с БД
- Управление памятью
- Временные изменения состояния
Контекстные менеджеры гарантируют, что __exit__ всегда вызовется, даже при исключениях. Это критично для надёжного управления ресурсами.