使用 Django QuerySets 时使用列表推导而不是 for 循环
Posted
技术标签:
【中文标题】使用 Django QuerySets 时使用列表推导而不是 for 循环【英文标题】:Using list comprehension instead of for loop when working with Django QuerySets 【发布时间】:2011-10-06 13:28:40 【问题描述】:我正在尝试调整一个在速度部门遇到问题的应用程序。因此,我已开始尽可能将所有 for 循环语句转换为列表推导式。
目前,我正在研究一个需要遍历 Django 查询集字典的函数。旧代码使用 for-loop 语句来迭代它并且它工作正常。我使用列表理解的代码返回 django 查询集而不是我的模型对象。
代码如下:
def get_children(parent):
# The following works
children = []
for value in get_data_map(parent).itervalues():
children += list(value)
# This part doesn't work as intended.
booms = [value for value in get_data_map(parent).itervalues() if value]
import pdb
pdb.set_trace()
(Pdb) type(children[0])
<class 'site.myapp.models.Children'>
(Pdb) type(booms[0])
<class 'django.db.models.query.QuerySet'>
注意 get_data_map 返回一个字典,其值为<class 'django.db.models.query.QuerySet'>
这部分代码是应用程序中最耗时的部分之一。如果我在列表推导上进行这项工作,应用程序速度有望提高两倍。
知道如何加快这部分代码的速度吗?
【问题讨论】:
是什么让您认为列表解析比执行常规for ... in ...
循环更快?
我对生成器、列表推导、for 循环和 map 做了一个简单的测试,列表推导比 for .. 循环快了将近两倍。
【参考方案1】:
同意很难准确判断到底发生了什么,但是使用 django 调试工具栏可以大大简化调试 django 速度问题:
https://github.com/django-debug-toolbar/django-debug-toolbar
如果问题是 db hits 太多,请查看工具栏上的“SQL 查询”选项卡,它会非常清楚地显示出来。
【讨论】:
【参考方案2】:您的问题不是 for
循环与列表推导(最好是生成器)。您的问题是对数据库的查询太多。
由于您尝试获取一个列表,因此您应该尝试从一个查询中获取它。如果您解释了典型 QuerySet 中的内容,我们可以向您展示如何最好地合并它们。也许在 Q 对象上使用 OR
合并。或者可能建立一组整数并将其提供给__in=
过滤器。
【讨论】:
假设这是不可解决的,并且 OP 实际上并没有使用整个列表, itertools.chain 会使其更快,因为不是每个查询集都会导致查询(因为懒惰评估)。 是的,考虑到这么少的信息,真的很难说该怎么做。但是,我发现查询开销比 Django 中的循环开销要大得多。【参考方案3】:您的解决方案不起作用,因为您正在创建查询集列表。每个值都是一个查询集,您无处可加入它们。比较:
a = [1,2,3]
b = [x for x in a if x ]
你会期望 b 也是一个整数列表,对吧?您获得了查询集列表(比父列表更好),但您仍然需要加入它们。您可以使用 itertools.chain 执行此操作:
http://docs.python.org/library/itertools.html#itertools.chain
foos = itertools.chain(booms)
foos 应该是你想要的。
【讨论】:
以上是关于使用 Django QuerySets 时使用列表推导而不是 for 循环的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 QuerySets 和 MySql“全文搜索”在多个字段中进行 Django 搜索?
如何使用 Django Querysets 和 Q() 与相同模型类型的对象进行比较?