在Django中,假设我QuerySet
要遍历并打印结果,那么计算对象的最佳选择是什么?len(qs)
还是qs.count()
?
(此外,考虑到在相同的迭代中对对象进行计数也是不可行的。)
Answers:
在len()
和之间进行选择count()
取决于情况,有必要深入了解它们如何正确使用它们。
让我为您提供一些方案:
(最关键的)当您只想知道元素的数量并且不打算以任何方式处理它们时,至关重要的是使用count()
:
要做: queryset.count()
-这将执行单个SELECT COUNT(*) some_table
查询,所有计算都在RDBMS端进行,Python只需要以固定成本O(1)检索结果编号。
不要: len(queryset)
-这将执行SELECT * FROM some_table
查询,获取整个表O(N),并需要额外的O(N)内存来存储它。这是最糟糕的事情
无论如何,当您打算获取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
计数:
queryset.count() # this will perform an extra db query - len() did not
for obj in queryset: # fetching data
pass
恢复第二种情况(已获取查询集时):
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)
一眼就能看到一切:
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)
Django文档中的良好参考:
QuerySet
上下文发布实现。
对于喜欢测试测量的人(Postresql):
如果我们有一个简单的Person模型及其1000个实例:
class Person(models.Model):
name = models.CharField(max_length=100)
age = models.SmallIntegerField()
def __str__(self):
return self.name
平均而言,它给出:
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)
因此,您如何看到比该特定测试用例快count()
近2倍的速度len()
。
总结其他人已经回答的问题:
len()
将获取所有记录并对其进行迭代。count()
将执行SQL COUNT操作(处理大型查询集时要快得多)。的确,如果在执行此操作之后,整个查询集将被迭代,那么从整体上来说,使用它可能会稍微更有效len()
。
然而
在某些情况下,例如,在有内存限制的情况下,可以方便地(在可行时)拆分对记录执行的操作。可以使用django分页来实现。
然后,使用count()
将是一种选择,并且您可以避免必须一次获取整个查询集。