Кэширует ли @ properties decorator результаты?
Моя IDE имеет"исправленный" мой код для преобразования функции (и другого кода) в свойство. Я беспокоюсь, что это может быть неэффективно.
@property
def output_all_children(self):
lh = ListHolder()
traverse_directories(self.start_directory, lh)
return lh.internal_list
Это делает некоторый тяжелый подъем ввода / вывода и занимает некоторое время. Я задаюсь вопросом, является ли это неправильным из-за соображений эффективности. Мне интересно, не кэшируются ли результаты,как я надеялся.
Если к этому свойству обращаться несколько раз, оно будет перестраиваться и возвращать lh.internal_list каждый раз?
Я бы поправил это путем наличия переменной уровня класса и обновления ее при самостоятельной работе.изменен каталог start_directory.
Я посмотрел на это:
Как создать декоратор для ленивой инициализации свойства и он ссылается на свойство только для чтения, в то время как мое было бы обновленным свойством
Пожалуйста, никаких комментариев по поводу слепого доверия Идам. Я знаю, и это мышление подсказало мне этот вопрос.
2 ответов:
Нет, свойство просто преобразует доступ к атрибуту в вызов функции. Автоматическое кэширование не происходит.
Другими словами, синтаксисinstance.output_all_childrenпереводится вinstance.output_all_children()для вас.Вы можете легко добавить некоторое кэширование к методу свойств или повторно использовать код, на который вы ссылаетесь в своем вопросе; просто замените метод
__set__для обработки присвоения атрибутов, если вы этого хотите.Добавление кэширования:
_output_all_children = None @property def output_all_children(self): if self._output_all_children is None: lh = ListHolder() traverse_directories(self.start_directory, lh) self._output_all_children = lh.internal_list return self._output_all_children
Действительно, свойство будет вызываться каждый раз. Это не очень хорошее использование свойства; вызывающие ожидают, что оно займет не намного больше времени, чем доступ к атрибуту.
В пирамиде есть прекрасный декоратор под названием
reify, который вызывает функцию в первый раз, а затем устанавливает значение в качестве одноименного атрибута для объекта, чтобы в следующий раз использовалось ранее вычисленное значение.Если вы не хотите использовать пирамиду только для одного декоратора, вы можете использовать эту версию, которую я написал (it работает как пирамида, но я написал его самостоятельно):
import functools def reify(func): class Descriptor(object): def __get__(self, inst, type=None): val = func(inst) setattr(inst, func.__name__, val) return val return functools.wraps(func)(Descriptor())Чтобы он хорошо работал с вашим вариантом использования, просто
delатрибут из экземпляра, когда вам нужно его пересчитать:@reify def all_children(self): lh = ListHolder() traverse_directories(self.start_directory, lh) return lh.internal_list # for internal use only, call when a cached all_children may no longer be valid def _invalidate_all_children(self): try: del self.all_children except AttributeError: pass
Comments