Дескриптор Python: однократная запись и setname

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

Другие упражнения по этой теме: Дескрипторы Python: задачи на get, set и set_name.

Регистры однократной записи

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

При первом присваивании дескриптор проверяет тип переданного значения и сохраняет его в локальном хранилище объекта. Любая повторная попытка перезаписать уже установленный регистр в том же экземпляре должна немедленно вызывать ValueError, оставляя прежнее значение нетронутым. Если же значение регистра попытаются прочесть до первой записи, обращение завершается стандартным AttributeError. При доступе к атрибуту через сам класс возвращается непосредственно объект-дескриптор.

Реализуйте валидирующий дескриптор однократной записи, обеспечивающий такое поведение регистров.

Что уже дано

В редакторе объявлен класс WriteOnce с заготовками методов __init__, __set_name__, __get__ и __set__. Вспомогательные функции для тестирования протокола доступны во время исполнения скрипта, реализовывать их заново не требуется.

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

Реализуйте data-дескриптор WriteOnce(expected_type: type):

  • Конструктор принимает тип expected_type, которому обязаны соответствовать записываемые значения.
  • Метод __set_name__(self, owner, name) автоматически сохраняет имя поля при связывании с классом-владельцем.
  • До первого успешного присваивания атрибут считается неинициализированным в каждом объекте-владельце независимо. Чтение неинициализированного атрибута через экземпляр возбуждает AttributeError.
  • При первом присваивании дескриптор проверяет тип: isinstance(value, self.expected_type). Если передан неподходящий тип, возбуждается TypeError, а атрибут остаётся неинициализированным.
  • После первого успешного присваивания любая последующая попытка записать новое значение в тот же экземпляр возбуждает ValueError (даже если тип нового значения неверен), а исходное сохранённое значение не меняется.
  • Ложные по булевой логике значения (0, False, пустые строки, коллекции и None) являются полноценными корректными значениями, если они удовлетворяют expected_type.
  • При обращении к атрибуту через класс-владелец (instance is None) метод __get__ возвращает сам объект-дескриптор self.
  • Разные атрибуты одного класса и независимые экземпляры класса не должны делить значения между собой.

Ввод и вывод

Потоки stdin и stdout не используются: считывать ввод и вызывать print() не нужно. Тестирующая система извлекает класс верхнего уровня WriteOnce после выполнения кода ученика и проверяет его поведение на классах-владельцах с 1–10 дескрипторами, сериями из 1–100 объектов и до 1000 операций в серии.

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

class Controller:
    gain = WriteOnce(int)

controller = Controller()
try:
    print(controller.gain)
except AttributeError:
    print("AttributeError")
controller.gain = 7
print(controller.gain)
try:
    controller.gain = 9
except ValueError:
    print("ValueError")
print(controller.gain)

Вывод:

AttributeError
7
ValueError
7
РешениеPython

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

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

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