Контекстный менеджер Python: откат списания ресурсов при ошибке
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Контекстные менеджеры Python: задачи на with, откат и исключения.
Реестр снаряжения
База проката оформляет комплексную выдачу экипировки для туристической группы. Инвентарь списывается из центрального реестра поштучно прямо во время подбора снаряжения. Если в процессе комплектации нужной вещи не оказывается на складе или клиент внезапно отказывается от поездки, оформление срывается исключением.
Чтобы не оставлять реестр в повреждённом состоянии, операцию оборачивают в менеджер контекста на базе класса. Он ведёт учёт каждой снятой с баланса единицы по ходу выполнения блока. Если оформление прерывается любой ошибкой, менеджер должен автоматически вернуть на склад ровно те позиции и в том количестве, которое успела списать текущая операция, не подавляя и не заменяя возникшее исключение.
Реализуйте класс менеджера контекста, который страхует реестр проката и при аварийном выходе восстанавливает баланс только затронутого снаряжения.
Что уже дано
В заготовке объявлен класс EquipmentLedger с заготовками методов __init__, __enter__, take и __exit__, которые вам предстоит реализовать.
Что нужно сделать
Реализуйте класс EquipmentLedger(stock: dict[str, int]) со следующим поведением:
- Конструктор принимает изменяемый словарь
stock, где ключи — названия предметов снаряжения (строки длиной от 1 до 100 символов, всего от 0 до 200 ключей), а значения — их неотрицательные целочисленные остатки на складе (от 0 до 1 000 000). Отсутствие ключа означает, что доступных единиц предмета нет (0). - Метод
__enter__()активирует контекст и возвращает сам экземпляр менеджера (self). - Метод
take(item: str)выполняет поштучное списание (в рамках контекста выполняется от 0 до 1000 операций): - если остаток
itemв переданном словареstockстрого больше нуля, метод немедленно уменьшает этот остаток ровно на1и записывает списание на счёт текущего менеджера; - если предмета нет в словаре или его остаток равен нулю, метод возбуждает исключение
LookupError(item), ничего не списывая и не фиксируя. - При нормальном завершении блока
withвсе выполненные списания остаются в силе. - Если внутри блока
withвозникает любое исключение (включая сбой самого методаtakeили внешние ошибки), метод__exit__(exc_type, exc_val, exc_tb)возвращает на склад ровно те единицы, которые были успешно списаны этим экземпляром менеджера, после чего исключение обязано беспрепятственно всплыть дальше (ошибки не подавляются). - Откат восстанавливает только «свои» списания: любые независимые изменения словаря
stock, сделанные другим кодом или другим менеджером во время работы блока (как по затронутым, так и по посторонним ключам), должны сохраниться. - Никаких отложенных копий при успехе: списания происходят по месту прямо во время вызова
take.
Ввод и вывод
Стандартный поток ввода stdin не используется. Печатать через print в stdout ничего не нужно: после выполнения вашего кода тестирующая система считывает из глобального пространства имён класс верхнего уровня EquipmentLedger, создаёт экземпляры менеджера вокруг общих словарей остатков и проверяет их поведение внутри и после блоков with.
Пример использования
stock = {'tent': 2, 'lamp': 3}
try:
with EquipmentLedger(stock) as rental:
rental.take('tent')
stock['lamp'] += 1
raise RuntimeError('cancelled')
except RuntimeError:
pass
print(stock)
Вывод:
{'tent': 2, 'lamp': 4}
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- exit в Python: аргументы исключения и подавление ошибки
- Контекстный менеджер Python: подавление выбранных типов исключений
- Контекстный менеджер Python: вложенные транзакции и глубина входа
- Контекстный менеджер Python: временная подмена списка с восстановлением
- Проверка контекстного менеджера Python: подавление и замена исключения
- Contextmanager на Python: транзакционные изменения словаря с квотой
