直接看cprofile中sorted、list.sort、__lt__的tottime占比;若调用次数上万且单次不长,多因循环内重复排序或key函数含io/重复计算。

怎么快速判断是不是 sorted() 或 list.sort() 拖慢了程序
直接看耗时占比最有效。用 cProfile 跑一次,重点盯 sorted、list.sort、__lt__ 这几行的累计时间(tottime)。如果单次调用不长但调用次数上万,问题往往出在循环里反复排序——比如在 for 循环里对同一份数据反复调用 sorted(data)。
常见错误现象:
- CPU 占用高,但没明显 I/O 或网络等待
- 数据量从 1 万增到 10 万,耗时暴涨 100 倍(疑似 O(n²) 行为)
- 日志里发现某函数被调用几万次,而它内部含 sorted()
- 先用
python -m cProfile -s tottime your_script.py快速定位热点 - 确认是否真在排序:有些“排序感”强的操作其实是
heapq.nlargest()或字典推导,别一概归给sorted - 注意装饰器或 ORM 查询链里隐式触发的排序,比如 Django 的
.order_by()后接 Python 端二次处理
key 函数写错导致性能雪崩的典型表现
Python 排序本身是 Timsort,稳定且高效,但 key 函数一旦涉及重复计算、IO 或深拷贝,就会把 O(n log n) 拉成 O(n² log n)。最典型的是在 key 里查数据库、读文件、或每次调用都新建大对象。
使用场景举例:
- 按文件修改时间排序:sorted(files, key=os.path.getmtime) —— 看似简洁,实则每比对一次就触发一次系统调用
- 按模型字段排序:sorted(items, key=lambda x: x.expensive_method()) —— 方法内部有缓存缺失或 DB 查询
- 把
key计算提到排序前:用[(key_func(x), x) for x in data]预计算,再排序,最后解包 - 加缓存:对纯函数用
@lru_cache,但注意参数不能含不可哈希对象(如 dict、list) - 避免在
key里做字符串切片 + 正则匹配:正则编译应提至外层,切片尽量用str.startswith()这类常数操作
小数据量下 sorted() 和 list.sort() 的开销差异容易被忽略
两者算法一致,但 sorted() 必须新建列表,分配内存 + 复制引用;list.sort() 是原地操作。当列表元素是大对象(如 Pandas DataFrame、嵌套 dict),复制开销会明显——不是 CPU 时间,而是内存带宽和 GC 压力。
性能影响:
- sorted(big_list) 在 10 万条、每条 1KB 的数据上,额外内存占用 ≈ 100MB
- 若后续立刻丢弃原列表,却仍用 sorted(),纯属浪费
- 能原地改就用
list.sort(),尤其在 pipeline 中间步骤 - 若需保留原顺序,考虑是否真要完整副本:有时只需索引排序(
numpy.argsort()或range(len(data))配key) - 对只读场景,用生成器表达式 +
heapq替代全量排序(如取 Top-K)
自定义类排序时 __lt__ 实现不当引发的隐形卡顿
Python 3+ 排序只依赖 __lt__(小于比较),但很多人写成调用其他方法或重复解析字段,导致每次比较都做冗余工作。更隐蔽的是,如果 __lt__ 返回 NotImplemented 或抛异常,Python 会退回到反射调用,性能断崖下跌。
功能列表:底层程序与前台页面分离的效果,对页面的修改无需改动任何程序代码。完善的标签系统,支持自定义标签,公用标签,快捷标签,动态标签,静态标签等等,支持标签内的vbs语法,原则上运用这些标签可以制作出任何想要的页面效果。兼容原来的栏目系统,可以很方便的插入一个栏目或者一个栏目组到页面的任何位置。底层模版解析程序具有非常高的效率,稳定性和容错性,即使模版中有错误的标签也不会影响页面的显示。所有的标
立即学习“Python免费学习笔记(深入)”;
错误示例:def __lt__(self, other): return self.to_dict()['score'] —— 每次比对都序列化两次
-
__lt__内必须是轻量、无副作用的属性访问,比如self._score - 确保所有参与排序的实例都已预计算好比较所需字段,不要懒加载
- 测试是否真的走
__lt__:临时在方法里加print("hit"),看输出频次是否符合预期(n log n 级别,不是 n²)
复杂点在于:排序瓶颈常常不在算法本身,而在你没意识到的上下文——比如 key 函数里一次数据库查询,在 10 万次比较中被调用了 10 万次。盯着函数调用频次,比盯着算法复杂度更管用。










