python多条件排序必须用key参数返回元组,cmp已废弃;key中按优先级排列字段,数值降序加负号,none需兜底处理,复杂逻辑应拆为命名函数。

Python多条件排序用key参数,别碰cmp
Python 3 已彻底移除list.sort(cmp=...)和sorted(..., cmp=...),cmp函数不再合法。所有自定义排序必须转为key函数——它返回一个可比较的元组或对象,由Python内置比较逻辑处理。
常见错误现象:TypeError: must use keyword argument for key function(传了cmp但没意识到已被弃用),或写了个cmp函数却卡在sorted(..., cmp=my_cmp)报错。
-
key函数必须返回一个值(通常是tuple),不是int(-1/0/1) - 多个条件按优先级从左到右放元组里:
key=lambda x: (x.age, -x.score, x.name.lower()) - 升序直接写字段,降序加负号(数值)或用
reversed(字符串慎用,需明确语义)
用functools.cmp_to_key包装旧cmp函数
只有当你手头已有现成的双参数cmp(a, b)函数(比如从Python 2迁移、或调用第三方库返回的比较器),才需要cmp_to_key。它不提升性能,只是兼容桥接。
使用场景:重用一段逻辑复杂的三路比较(比如按中文拼音首字母分组再排序),且不愿/不能改写为key函数。
立即学习“Python免费学习笔记(深入)”;
-
cmp_to_key返回的是一个“键对象”,只能用于key=...参数,不能当普通函数调用 - 不要对非数值类型盲目取负实现降序:
cmp_to_key(lambda a,b: -my_old_cmp(a,b))是错的,应改在原cmp里反转逻辑 - 性能上比纯
key慢约10%–20%,因每次比较都要构造临时对象并调用__call__
示例:
from functools import cmp_to_key
def my_cmp(a, b):
if a['score'] != b['score']:
return b['score'] - a['score'] # 高分在前
return -1 if a['name'] < b['name'] else (1 if a['name'] > b['name'] else 0)
data.sort(key=cmp_to_key(my_cmp))
key函数里怎么处理None或类型不一致?
真实数据常含None、空字符串、混合类型(如'1' vs 1),直接参与元组比较会抛TypeError。
关键原则:让key函数内部做类型归一和缺省兜底,而不是依赖外部清洗。
- 对可能为
None的字段,用or提供默认值:lambda x: (x.updated_at or datetime.min, x.priority or 0) - 字符串数字要转
int/float,否则按ASCII排('10' 为<code>True) - 避免在
key里做耗时操作(如DB查询、正则匹配),排序本身会被反复调用
复杂逻辑别硬塞进lambda,拆成命名函数
当排序规则涉及多步判断(比如“状态优先级 > 创建时间倒序 > 用户等级 > 昵称拼音”),把key写成单行lambda会难读、难测、难复用。
容易踩的坑:调试时无法打日志、IDE无法跳转、单元测试没法覆盖分支路径。
- 命名函数名要有语义,比如
sort_key_for_admin_queue,别叫get_key - 函数内可用
if/elif/else构造元组,比嵌套or和三元更清晰 - 如果逻辑随上下文变化(如用户偏好控制升/降序),把方向作为参数传入,而非拼接字符串或动态生成
key
真正麻烦的从来不是语法,而是当key函数要同时处理缺失字段、国际化排序、业务权重叠加、以及前端传来的动态排序字段列表时——这时候你得决定:是在key里做全量判断,还是提前规整数据结构。后者通常更稳。










