代码之家  ›  专栏  ›  技术社区  ›  orokusaki

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

  •  5
  • orokusaki  · 技术社区  · 14 年前

    在下面的例子中, cached_attr 用于在数据库昂贵的属性( related_spam 在本例中)被调用。在这个例子中,我使用 cached_spam 以保存查询。我在设置和获取值时都会输出打印语句,以便进行测试。我通过一个 Egg 在视图中和视图中使用 {{ 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 回复  |  直到 14 年前
        1
  •  9
  •   Daniel Roseman    14 年前

    你的代码没什么问题。问题可能不在这里,而是在如何使用代码方面。

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

    例如,假设您有一个RelatedObject类,其ForeignKey为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
    

    所以,把缓存填满 我的第一个蛋 没有填满它 我的第二个蛋 .

    当然,对象不会在请求之间持久化(除非它们被特别设置为全局的,这很可怕),所以缓存也不会持久化。

        2
  •  1
  •   Tobu    14 年前

    可扩展的Http服务器没有任何共享;您不能依赖任何单一的东西。要共享状态,您需要连接到一个特殊用途的服务。

    Django's公司 caching support 适合您的用例。它也不一定是全局单例;如果使用 locmem:// ,它将是本地进程,这可能是更有效的选择。