Python - кэширование свойства, чтобы избежать будущих вычислений - PullRequest
5 голосов
/ 27 ноября 2010

В следующем примере cached_attr используется для получения или установки атрибута в экземпляре модели, когда вызывается дорогостоящее свойство базы данных (в данном примере related_spam).В этом примере я использую cached_spam для сохранения запросов.Я ставлю операторы печати при настройке и при получении значений, чтобы я мог проверить это.Я протестировал его в представлении, передав экземпляр Egg в представление и представление, используя {{ egg.cached_spam }}, а также другие методы модели Egg, которые сами вызывают cached_spam.Когда я закончил и протестировал его, вывод оболочки на сервере разработки Django показал, что кэш атрибутов пропускался несколько раз, а также успешно получался несколько раз.Это кажется противоречивым.С теми же данными, когда я вносил небольшие изменения (такие же, как изменение строки оператора печати) и обновлял (со всеми одними и теми же данными), происходило различное количество промахов / успехов.Как и почему это происходит?Этот код неправильный или очень проблемный?

class Egg(models.Model):
    ... fields

    @property
    def related_spam(self):
        # Each time this property is called the database is queried (expected).
        return Spam.objects.filter(egg=self).all()  # Spam has foreign key to Egg.

    @property
    def cached_spam(self):
        # This should call self.related_spam the first time, and then return
        # cached results every time after that.
        return self.cached_attr('related_spam')

    def cached_attr(self, attr):
        """This method (normally attached via an abstract base class, but put
        directly on the model for this example) attempts to return a cached
        version of a requested attribute, and calls the actual attribute when
        the cached version isn't available."""
        try:
            value = getattr(self, '_p_cache_{0}'.format(attr))
            print('GETTING - {0}'.format(value))
        except AttributeError:
            value = getattr(self, attr)
            print('SETTING - {0}'.format(value))
            setattr(self, '_p_cache_{0}'.format(attr), value)
        return value

Ответы [ 2 ]

9 голосов
/ 27 ноября 2010

Ничего плохого в вашем коде, насколько это возможно.Возможно, проблема не в этом, но в том, как вы используете этот код.

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

Например, если у вас есть класс RelatedObject с ForeignKey to Egg:

my_first_egg = Egg.objects.get(pk=1)
my_related_object = RelatedObject.objects.get(egg__pk=1)
my_second_egg = my_related_object.egg

Здесь my_first_egg и my_second_egg оба ссылаются на строку базы данных с pk 1, но они не один и тот же объект:

>>> my_first_egg.pk == my_second_egg.pk
True
>>> my_first_egg is my_second_egg
False

Итак, заполнение кеша на my_first_egg не заполняет его на my_second_egg.

И, конечно,объекты не сохраняются в запросах (если только они специально не сделаны глобальными, что ужасно), поэтому кеш также не сохраняется.

1 голос
/ 27 ноября 2010

Http-серверы, которые масштабируются совместно, ничего;Вы не можете полагаться ни на что, будучи синглтоном.Чтобы поделиться состоянием, вам необходимо подключиться к специализированной службе.

Поддержка кэширования Django подходит для вашего случая использования.Это не обязательно глобальный синглтон;если вы используете locmem://, это будет локальный процесс, что может быть более эффективным выбором.

...