Python - 缓存属性以避免将来计算

时间:2010-11-26 22:55:12

标签: python django caching attributes properties

在以下示例中,cached_attr用于在调用数据库昂贵的属性(示例中为related_spam)时获取或设置模型实例上的属性。在示例中,我使用cached_spam来保存查询。我在设置和获取值时放置了print语句,以便我可以测试它。我在一个视图中测试了它,方法是使用Egg{{ egg.cached_spam }}实例传递到视图和视图中,以及Egg模型上调用cached_spam的其他方法} 他们自己。当我完成并测试它时,Django的开发服务器中的shell输出显示属性缓存被错过了好几次,并成功获得了几次。这似乎是不一致的。使用相同的数据,当我进行小的更改(只需更改print语句的字符串)并刷新(使用所有相同的数据)时,会发生不同数量的未命中/成功。这是怎么发生的?这段代码不正确或有问题吗?

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 个答案:

答案 0 :(得分:9)

就你的代码而言,没有任何问题。问题可能不在那里,而在于你如何使用该代码。

要实现的主要事情是模型实例没有标识。这意味着如果你在某个地方实例化一个Egg对象,而在其他地方实例化另一个,即使它们引用相同的底层数据库行,它们也不会共享内部状态。因此,在一个上调用cached_attr不会导致在另一个中填充缓存。

例如,假设您有一个带有ForeignKey to Egg的RelatedObject类:

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_eggmy_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 :(得分:1)

扩展的Http服务器是无共享的;你不能依赖任何单身人士。要共享状态,您需要连接到特殊用途的服务。

Django的caching support适合您的用例。它也不一定是全球单身人士;如果你使用locmem://,它将是进程本地的,这可能是更有效的选择。