Дескрипторы Python: валидация и сброс кэша зависимых свойств
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Дескрипторы Python: задачи на get, set и set_name.
Инвалидация кэша в дескрипторе координат
Картографический сервис строит пешеходные треки, отображая для каждого маршрута общую длину дистанции и ориентировочное время в пути. Сами координаты точек обновляются через валидируемый data-дескриптор, а затратные расчёты длины и времени оформлены как ленивые свойства, чтобы не перегружать процессор при частых мелких правках.
Когда в маршрут успешно записывается новый список точек, дескриптор сохраняет его в экземпляре и автоматически удаляет из словаря этого же объекта кэшированные результаты всех явно зарегистрированных зависимых свойств. Если какое-то из них ещё ни разу не запрашивалось, обновление координат всё равно проходит штатно без ошибок, а последующее обращение к любому зависимому показателю честно запускает его вычисление уже по новым данным.
Реализуйте data-дескриптор первичного поля, который при каждом успешном присваивании инвалидирует список связанных ленивых свойств в словаре экземпляра.
Что уже дано
В среде выполнения уже объявлены вспомогательные инструменты для тестирования: non-data дескриптор LazyValue, фабрика валидатора make_cache_probe() и фабрика тестового класса make_cache_board(). Заново реализовывать их не нужно.
Вам предстоит заполнить класс CacheInvalidatingField методами __init__, __set_name__, __get__ и __set__.
Что нужно сделать
Реализуйте data-дескриптор CacheInvalidatingField(validator, dependent_names) со следующими методами:
__init__(self, validator, dependent_names):validator— вызываемый объект для проверки новых значений;dependent_names— последовательность строковых имён зависимых атрибутов, кэш которых нужно сбрасывать при обновлении поля (от 0 до 16 имён, возможны дубликаты).__set_name__(self, owner, name):- запоминает имя поля
name, связывая дескриптор с классом-владельцем. __set__(self, instance, value):- сначала однократно вызывает
validator(value). Если валидатор возбуждает исключение, дескриптор не перехватывает его, оставляет значение поля и кэши вinstance.__dict__нетронутыми и прекращает операцию; - при успешной валидации (нормальный возврат из
validator) сохраняет исходное переданное значениеvalueв словаре текущего экземпляраinstance.__dict__под закрытым ключом, состоящим из символа подчёркивания и имени поля:f"_{name}"; - удаляет из словаря
instance.__dict__все зарегистрированные зависимые именаdependent_names. Если какого-то имени в словаре объекта нет, это не считается ошибкой. Не зарегистрированные зависимые имена удаляться не должны; - изменения применяются строго к целевому объекту
instanceи не затрагивают другие экземпляры. Принудительный расчёт зависимых свойств при присваивании не выполняется. __get__(self, instance, owner=None):- при обращении через класс (
instance is None) возвращает сам объект дескриптора (self); - при обращении через экземпляр возвращает ранее сохранённое значение по закрытому ключу
f"_{name}"; - если полю в этом экземпляре значение ещё ни разу успешно не присваивалось, возбуждает
AttributeError.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля класс верхнего уровня CacheInvalidatingField и создаёт класс-владелец с дескриптором в его пространстве имён, благодаря чему Python автоматически вызывает __set_name__.
Пример использования
probe = make_cache_probe()
field = CacheInvalidatingField(probe.validate, ("length", "duration"))
board = make_cache_board(field, "points", ("length", "duration"), 1)
route = board.instances[0]
route.points = [1, 2]
_ = route.length
print("length" in route.__dict__)
route.points = [9]
print("length" in route.__dict__)
_ = route.length
print(board.calls[0]["length"])
Вывод:
True False 2
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Дескриптор и dict в Python: кто имеет приоритет при чтении
- Дескрипторы Python: почему значение разделяется между экземплярами
- Дескрипторы Python: значения по умолчанию и удаление переопределений
- Дескриптор Python: однократная запись и setname
- Дескриптор Python: проверка связанных полей low и high
- Дескриптор Python без get: журнал присваиваний в set
