Класс-декоратор Python: баланс доступных вызовов функции

Напишите решение на Python и проверьте его на странице.

Другие упражнения по этой теме: Классы-декораторы Python: задачи на call, методы и состояние.

Накопитель зарядов заклинания

В настольной тактической игре заклинание сопряжено с накопителем зарядов. Изначально баланс равен нулю, поэтому без предварительной зарядки применить магию нельзя: попытка активации с нулевым запасом не вызывает целевую функцию, а сразу завершается служебным исключением опустошения.

Мастер игры может в любой момент пополнить накопитель через отдельный метод декоратора, прибавив нужное число зарядов к текущему остатку без сброса уже накопленных единиц. Если же заклинание запускается при положительном балансе, оно исполняется, а ровно один заряд списывается строго после успешного завершения. Если во время работы заклинания возникает ошибка, заряд не расходуется, а само исключение беспрепятственно всплывает наружу.

Реализуйте класс-декоратор с методом пополнения баланса, который защищает функцию от вызова при пустом накопителе и списывает заряд только после её успешного выполнения.

Что уже дано

При выполнении кода уже объявлен класс исключения EmptyCharges, унаследованный от LookupError. Также доступны вспомогательные функции make_call_probe и observe_call_probe для отслеживания вызовов, которые не нужно реализовывать самостоятельно.

Вам предстоит реализовать методы класса SpellCharges.

Что нужно сделать

Реализуйте класс-декоратор SpellCharges со следующим поведением:

  • Конструктор __init__(self, func) принимает целевую функцию func (обычный вызываемый объект уровня модуля) и сохраняет её. При создании каждый экземпляр начинает работу с нулевым балансом зарядов (0).
  • Метод add_charges(self, amount) принимает целое неотрицательное число amount (от 0 до 1000) и прибавляет его к текущему остатку зарядов. Требований к возвращаемому значению метода нет.
  • Метод __call__(self, *args, **kwargs) обрабатывает вызов задекорированного заклинания:
  • Если текущий баланс зарядов равен 0, декорированная функция func не вызывается вовсе; метод сразу возбуждает исключение EmptyCharges() без обязательного сообщения.
  • Если текущий баланс строго больше 0, метод вызывает исходную функцию func(*args, **kwargs) с неизменными позиционными и именованными аргументами.
  • Если вызов функции func завершился успешно, метод списывает с баланса ровно 1 заряд и возвращает результат выполнения func.
  • Если вызов функции func привёл к исключению, баланс зарядов остаётся неизменным (заряд не тратится), а исходный объект исключения распространяется дальше без перехвата или подмены.

Правила работы и ограничения:

  • Суммарное количество операций вызова и пополнения не превышает 1000.
  • Декорирование методов классов, связывание контекста (__get__), время жизни зарядов (TTL), приоритеты, формулы расчёта маны и повторные вызовы при ошибках реализовывать не требуется.
  • Хранить баланс в публичном атрибуте с фиксированным именем не требуется.

Ввод и вывод

Стандартные потоки stdin и stdout не используются: считывать ввод и выводить данные через print() не требуется.

После выполнения вашего скрипта проверяющая система находит в глобальной области видимости класс верхнего уровня SpellCharges, оборачивает им тестовые функции и напрямую проверяет вызовы, пополнение зарядов и обработку исключений.

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

probe = make_call_probe([["return", "light"]])
spell = SpellCharges(probe.compute)
spell.add_charges(1)
print(spell())
print(observe_call_probe(probe)["call_count"])

Вывод:

light
1
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи