You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

优化Django QuerySet词汇表生成的时间复杂度

优化Django QuerySet词汇表生成的时间复杂度

嗨,看了你的问题,你当前的代码因为两次遍历整个QuerySet,确实是O(n²)的时间复杂度,数据量越大效率越低。下面给你两个更高效的优化方案,都能把时间复杂度降到O(n),代码也更简洁:

方案一:Python层面一次遍历完成

这个方案不需要改动数据库查询逻辑,只需要单次遍历QuerySet就能完成分组,直接把时间复杂度降到O(n):

def get_toc(self):
    toc = {}
    qs = self.get_queryset()
    for q in qs:
        # 统一转成大写,避免同一个字母因大小写分成两组(比如A和a)
        first_char = q.title[0].upper()
        # 用setdefault省去判断键是否存在的冗余代码
        toc.setdefault(first_char, []).append(q)
    # 对首字母按键排序,保证词汇表的有序性
    return {key: toc[key] for key in sorted(toc.keys())}

整个过程只需要遍历一次QuerySet,相比原代码的两次循环,效率提升非常明显。

方案二:利用Django数据库查询优化分组(适合大数据量)

如果你的QuerySet数据量很大,把所有对象加载到内存会占用较多资源,这时候可以让数据库帮忙处理首字母提取和排序,进一步优化性能:

首先定义一个自定义数据库函数,用来提取标题的第一个字符:

from django.db.models import Func, CharField

class FirstChar(Func):
    function = 'LEFT'
    template = "%(function)s(%(expressions)s, 1)"
    output_field = CharField()

然后在方法里通过注解和数据库排序来实现分组:

def get_toc(self):
    # 给每个对象添加首字母注解,并按首字母+标题排序
    qs = self.get_queryset().annotate(
        first_char=FirstChar('title')
    ).order_by('first_char', 'title')
    
    toc = {}
    for item in qs:
        first_char = item.first_char.upper()
        toc.setdefault(first_char, []).append(item)
    
    return toc

这个方案的优势在于,数据库会先完成排序操作(如果title字段有索引,查询效率会更高),遍历的时候直接按顺序分组即可,同时能减少内存占用压力。

额外小提示

如果你的标题可能以数字、符号等非字母开头,可以加个判断把这类内容统一归类到#分组里,让词汇表更规整:

first_char = q.title[0].upper()
if not first_char.isalpha():
    first_char = '#'
toc.setdefault(first_char, []).append(q)

备注:内容来源于stack exchange,提问作者Viktor

火山引擎 最新活动