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

oro*_*aki 5 python django attributes caching 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
Run Code Online (Sandbox Code Playgroud)

Dan*_*man 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
Run Code Online (Sandbox Code Playgroud)

这里my_first_eggmy_second_egg两者都引用pk 1的数据库行,但它们不是同一个对象:

>>> my_first_egg.pk == my_second_egg.pk
True
>>> my_first_egg is my_second_egg
False
Run Code Online (Sandbox Code Playgroud)

因此,填充缓存my_first_egg不会填充它my_second_egg.

而且,当然,对象不会在请求中持续存在(除非它们是专门设置为全局的,这很可怕),因此缓存也不会持久存在.