Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/366.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python-缓存属性以避免将来的计算_Python_Django_Caching_Attributes_Properties - Fatal编程技术网

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

Python-缓存属性以避免将来的计算,python,django,caching,attributes,properties,Python,Django,Caching,Attributes,Properties,在下面的示例中,调用数据库昂贵属性(related\u spam)时,cached\u attr用于获取或设置模型实例上的属性。在本例中,我使用cached_spam保存查询。我在设置和获取值时都会使用打印语句,以便进行测试。我在视图中通过将Egg实例传递到视图中,并在视图中使用{{Egg.cached_spam},以及Egg模型上调用cached_spam本身的其他方法对其进行了测试。当我完成并测试它时,Django的开发服务器中的shell输出显示属性缓存丢失了好几次,并且成功地获得了好几次

在下面的示例中,调用数据库昂贵属性(
related\u spam
)时,
cached\u attr
用于获取或设置模型实例上的属性。在本例中,我使用
cached_spam
保存查询。我在设置和获取值时都会使用打印语句,以便进行测试。我在视图中通过将
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

就目前而言,代码没有问题。问题可能不在这里,但在于如何使用代码

要认识到的主要问题是模型实例没有标识。这意味着,如果在某个地方实例化一个Egg对象,而在另一个地方实例化另一个Egg对象,即使它们引用相同的底层数据库行,它们也不会共享内部状态。因此,对其中一个调用
cached\u 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
因此,在
myfirst\u egg
上填充缓存不会在
mysecond\u egg
上填充缓存


当然,对象不会在请求之间持久化(除非它们被专门设置为全局,这很可怕),因此缓存也不会持久化;你不能指望什么都是单身。要共享状态,您需要连接到专用服务


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

这只是针对每个请求的缓存。我永远不会尝试实现自己的缓存框架。我只是厌倦了在同一个请求中一遍又一遍地从数据库中提取对象。谢谢。我认为,提供的例子正是所发生的事情。我只是不知道在我的代码中哪里有这么多实例。根据我对代码的回顾,一个对象应该只有两个单独的实例(一个来自
my_related_object.egg
)有4个实例。我认为是时候对我的模型进行彻底的检修了;全部200行8(我找到了罪魁祸首!我有一个流氓方法再次获取一个对象的所有内容(我的编辑器的搜索没有找到它,因为由于过滤,语法有点不同)。有了这个,再加上我的缓存(简单的记忆)方法和一个巧妙的选择相关我将31个查询的结果减少到了3!!!再次感谢指针。我遇到了这个麻烦,并意识到解决这个问题的另一种方法是使用
with
模板标记为计算结果创建别名。@humble-thx,这实际上是一个非常有用的注释。