Contextmanager на Python: транзакционные изменения словаря с квотой
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Контекстные менеджеры Python: задачи на with, откат и исключения.
Черновик настроек с квотой правок
Служба управления параметрами синхронизирует пакеты правок с реестром настроек и следит за тем, чтобы разовая модификация не оказалась слишком масштабной. Для безопасного внесения изменений реестр не редактируют напрямую: генераторный менеджер контекста под декоратором @contextlib.contextmanager принимает целевой плоский словарь и целочисленную квоту на число расхождений, создаёт поверхностную копию оригинала и передаёт этот изолированный черновик в блок with.
Внутри блока с черновиком можно выполнять любые операции. Если при выполнении тела возник сбой, менеджер отбрасывает черновик, оставляет целевой словарь нетронутым и пропускает исключение дальше. При успешном же выходе из блока финальное состояние черновика сопоставляется с исходным снимком: каждое добавление ключа, изменение значения существующего ключа и явное удаление старого ключа засчитываются как одна единица расхождения.
Когда суммарное число таких расхождений не превышает заданную квоту, целевой словарь полностью заменяется содержимым черновика путём очистки и обновления. Если же квота строго превышена, фиксация изменений отменяется, целевой словарь не меняется, а менеджер возбуждает ValueError.
Реализуйте такой генераторный менеджер контекста для безопасного редактирования словаря настроек.
Что уже дано
В редакторе объявлена заготовка генераторной функции bounded_draft(target, quota) с декоратором @contextlib.contextmanager:
@contextlib.contextmanager
def bounded_draft(target, quota):
# TODO
raise NotImplementedError
yield
При тестировании также доступны вспомогательные функции проверки observe_dictionary_drafts и observe_empty_dictionary_draft, реализовывать их самостоятельно не требуется.
Что нужно сделать
Реализуйте генераторную функцию bounded_draft(target, quota) с декоратором @contextlib.contextmanager.
Функция принимает:
target— исходный плоский словарьdict[str, int](включая стандартные подклассыdictиint), содержащий от 0 до 20 строковых ASCII-ключей длиной от 1 до 30 символов со значениями от -1 000 000 до 1 000 000;quota— целое неотрицательное число от 0 до 20, задающее максимально допустимое количество расхождений.
Менеджер контекста должен работать по следующим правилам:
- При входе в блок
withсоздаётся независимая поверхностная копия целевого словаря, которая передаётся черезyield. Во время выполнения тела блока исходный объектtargetостаётся без изменений. - В теле блока над полученным черновиком могут производиться стандартные операции добавления, изменения значений, удаления ключей или очистки (до 1000 операций за сессию).
- Если внутри блока возникает любое исключение класса
BaseException, менеджер отбрасывает черновик, оставляет исходный словарьtargetнеизменным и беспрепятственно пропускает исключение наружу. - При нормальном выходе из блока подсчитывается итоговое количество расхождений между исходным состоянием
targetи финальным состоянием черновика. Каждое из следующих различий учитывается ровно один раз: - добавленный новый ключ;
- удалённый старый ключ;
- ключ, значение которого изменилось по сравнению с исходным. Если ключ был добавлен, а затем удалён, либо если значение ключа менялось, но в итоге вернулось к исходному значению, расхождением это не считается.
- Если итоговое число расхождений меньше либо равно
quota, менеджер контекста по месту обновляет исходный словарьtarget(сохраняя сам объект словаря), очищая его и наполняя актуальными записями черновика. - Если итоговое число расхождений строго больше
quota, менеджер контекста возбуждаетValueError(или его подкласс), не внося никаких изменений в исходный словарьtarget.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня bounded_draft и вызывает её с проверочными аргументами.
Пример использования
settings = {'a': 1}
with bounded_draft(settings, 2) as draft:
del draft['a']
draft['b'] = 2
print(settings)
try:
with bounded_draft(settings, 1) as draft:
draft['b'] = 3
draft['c'] = 4
except ValueError:
print(settings)
Вывод:
{'b': 2}
{'b': 2}
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- exit в Python: аргументы исключения и подавление ошибки
- Контекстный менеджер Python: подавление выбранных типов исключений
- Контекстный менеджер Python: вложенные транзакции и глубина входа
- Контекстный менеджер Python: откат списания ресурсов при ошибке
- Контекстный менеджер Python: временная подмена списка с восстановлением
- Проверка контекстного менеджера Python: подавление и замена исключения
