在Django QuerySet上计算vs len

ant*_*tam 76 python django performance

在Django中,假设我有一个QuerySet,我将迭代并打印结果,那么计算对象的最佳选择是什么?len(qs)或qs.count()?

(还假设在同一次迭代中计算对象不是一个选项)

And*_*den 110

虽然Django文档建议使用count而不是len:

注意:len()如果您只想确定集合中的记录数,请不要在QuerySets上使用.使用SQL可以更有效地处理数据库级别的计数SELECT COUNT(*),而Django count()正是出于这个原因提供了一种方法.

既然你正在迭代这个QuerySet,结果将被缓存(除非你正在使用iterator),因此最好使用len,因为这样可以避免再次访问数据库,也可能检索不同数量的结果!) .
如果您正在使用iterator,那么我会建议在迭代时使用计数变量(而不是使用计数),原因相同.


Krz*_*iek 40

在两者之间选择len()count()取决于具体情况,深入了解他们如何正确使用它们是值得的.

让我为您提供一些场景:

  1. (最重要的)当你只想知道元素的数量并且你不打算以任何方式处理它们时,使用它是至关重要的count():

    DO: queryset.count() - 这将执行单个SELECT COUNT(*) some_table查询,所有计算都在RDBMS端进行,Python只需要以固定成本O(1)检索结果号

    请勿: len(queryset) - 这将执行SELECT * FROM some_table查询,获取整个表O(N)并需要额外的O(N)内存来存储它.这是最糟糕的事情

  2. 无论如何,当您打算获取len()查询集时,使用它会稍微好一些,这不会导致额外的数据库查询count():

    len(queryset) # fetching all the data - NO extra cost - data would be fetched anyway in the for loop
    
    for obj in queryset: # data is already fetched by len() - using cache
        pass
    
    Run Code Online (Sandbox Code Playgroud)

    计数:

    queryset.count() # this will perform an extra db query - len() did not
    
    for obj in queryset: # fetching data
        pass
    
    Run Code Online (Sandbox Code Playgroud)
  3. 还原的第二种情况(已经提取了查询集):

    for obj in queryset: # iteration fetches the data
        len(queryset) # using already cached data - O(1) no extra cost
        queryset.count() # using cache - O(1) no extra db query
    
    len(queryset) # the same O(1)
    queryset.count() # the same: no query, O(1)
    
    Run Code Online (Sandbox Code Playgroud)

一旦你"瞥了一眼",一切都会很清楚:

class QuerySet(object):

    def __init__(self, model=None, query=None, using=None, hints=None):
        # (...)
        self._result_cache = None

    def __len__(self):
        self._fetch_all()
        return len(self._result_cache)

    def _fetch_all(self):
        if self._result_cache is None:
            self._result_cache = list(self.iterator())
        if self._prefetch_related_lookups and not self._prefetch_done:
            self._prefetch_related_objects()

    def count(self):
        if self._result_cache is not None:
            return len(self._result_cache)

        return self.query.get_count(using=self.db)
Run Code Online (Sandbox Code Playgroud)

Django文档中的好参考:

  • 出色的答案,+ 1用于在上下文中发布QuerySet实现。 (2认同)
  • 从字面上看,这是一个完美的答案。解释使用什么,更重要的是解释使用的“原因”。 (2认同)

小智 25

我认为使用len(qs)更有意义,因为你需要迭代结果.qs.count()如果您想要的所有内容都打印计数而不是迭代结果,那么这是一个更好的选择.

len(qs)将达到与数据库select * from table,而qs.count()将达到与分贝select count(*) from table.

qs.count()将给予退货整数,你不能迭代它


fun*_*man 5

对于喜欢测试测量的人(Postresql):

如果我们有一个简单的 Person 模型和它的 1000 个实例:

class Person(models.Model):
    name = models.CharField(max_length=100)
    age = models.SmallIntegerField()

    def __str__(self):
        return self.name
Run Code Online (Sandbox Code Playgroud)

在平均情况下,它给出:

In [1]: persons = Person.objects.all()

In [2]: %timeit len(persons)                                                                                                                                                          
325 ns ± 3.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit persons.count()                                                                                                                                                       
170 ns ± 0.572 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Run Code Online (Sandbox Code Playgroud)

那么你怎么能看到比这个特定的测试用例快count()2倍呢len()