Класс-декоратор Python: проверка чередования фаз вызова

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

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

Декоратор фаз привода

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

Контролировать этот порядок поручено специальному классу-декоратору. При каждом обращении он проверяет аргумент фазы: если вызывающий код нарушает последовательность (например, передаёт разгон дважды подряд), декоратор не запускает целевую функцию, а сразу возбуждает служебное исключение нарушения протокола.

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

Что уже дано

В редакторе объявлена заготовка класса PhaseGuard с методами __init__ и __call__.

При проверке также доступно исключение PhaseOrderError(RuntimeError), сигнализирующее об ошибке последовательности фаз. Реализовывать его самостоятельно не требуется.

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

Реализуйте класс PhaseGuard:

  • Конструктор __init__(self, function) принимает синхронный вызываемый объект function и сохраняет его для последующих запусков.
  • Метод __call__(self, phase, *args, **kwargs) принимает название фазы phase первым аргументом и перенаправляет остальные позиционные и именованные параметры без изменений.
  • Аргумент phase принимает значения 'accel' или 'hold' (включая пользовательские подклассы строки str).
  • При инициализации экземпляра допустимой считается исключительно фаза 'accel'.
  • Если значение phase не совпадает с ожидаемым на текущем шаге, метод обязан возбудить исключение PhaseOrderError (или его наследника) до вызова декорируемой функции. Сама функция при этом не вызывается, а текущее состояние ожидания не меняется.
  • Если фаза совпадает с ожидаемой, декоратор вызывает декорируемую функцию ровно один раз, передавая phase и все остальные аргументы *args, **kwargs как есть.
  • При успешном завершении целевой функции ожидаемая фаза переключается на противоположную ('accel' меняется на 'hold', а 'hold' — на 'accel'), а метод __call__ возвращает результат её работы.
  • Если целевая функция возбуждает любое исключение (семейства BaseException), это же исключение должно беспрепятственно всплыть выше. Ожидаемая фаза при этом не меняется и остаётся прежней.
  • Состояние конечного автомата изолировано для каждого экземпляра PhaseGuard. Разные экземпляры могут независимо обрабатывать до 1000 вызовов каждый.
  • Реализовывать сохранение метаданных функции или логику дескриптора для методов не требуется.

Ввод и вывод

Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля класс верхнего уровня PhaseGuard и вызывает его с проверочными аргументами.

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

guard = PhaseGuard(lambda phase, value: (phase, value * 2))
print(guard('accel', 3))
try:
    guard('accel', 4)
except PhaseOrderError:
    print('PhaseOrderError')
print(guard('hold', 4))

Вывод:

('accel', 6)
PhaseOrderError
('hold', 8)
РешениеPython

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

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

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