Functools -- cmp_to_key
cmp_to_key 用于把“老式比较函数(cmp 风格)”转换为“新式排序 key 函数”。
Python3 废弃了 cmp 参数,但你有时仍需要:
- 自定义排序规则
- 多条件复杂比较
- 非标准排序逻辑
- 与 Python2 保持兼容
这时就用:
from functools import cmp_to_key
Python3 的 sorted() 和 list.sort() 只能用:
- key=function
- reverse=True
不能传入 cmp=function。
但有些排序无法用 key 表达,例如:
- 比较两个元素之间关系
- 需要看两者的大小变化
- key 计算无法表示排序规则
例如:
- 按绝对值排序,但负数优先
- 复杂多级比较
- 中文拼音排序
- 自定义股票排序(如 00 开头优先)
- 自定义 None / 空值 的排序方式
这些无法用 key 函数表达。
from functools import cmp_to_key
def mycmp(a, b):
if a < b:
return -1 # a 在前
if a > b:
return 1 # b 在前
return 0 # 相等
nums = [5, 2, 9, 1]
print(sorted(nums, key=cmp_to_key(mycmp)))
输出:
[1, 2, 5, 9]
等效于普通排序,只是使用了自定义比较函数。
cmp_to_key 会将:
cmp(a, b) -> -1 / 0 / 1
转换为一个 “可比较的 Key 对象”,最终让:
sorted(key=cmp_to_key(...))
能够使用这个 key 的比较逻辑。
简化理解:
❗ cmp -> key
cmp_to_key 把 cmp 封起来,让排序觉得它是一个 key。
按字符串长度排序,但长度相同按字典序排
def mycmp(a, b):
if len(a) < len(b):
return -1
if len(a) > len(b):
return 1
return -1 if a < b else (1 if a > b else 0)
lst = ["python", "c", "go", "java"]
print(sorted(lst, key=cmp_to_key(mycmp)))
按股票代码排序(优先 00 -> 60 -> 其他)
def stock_cmp(a, b):
pri_a = 1 if a.startswith("00") else 2 if a.startswith("60") else 3
pri_b = 1 if b.startswith("00") else 2 if b.startswith("60") else 3
if pri_a != pri_b:
return -1 if pri_a < pri_b else 1
return -1 if a < b else 1 if a > b else 0
codes = ["300001", "600000", "000001", "601398", "002594"]
print(sorted(codes, key=cmp_to_key(stock_cmp)))
None 排到最后
def none_last(a, b):
if a is None: return 1
if b is None: return -1
return (a > b) - (a < b)
values = [4, None, 2, 9, None, 1]
print(sorted(values, key=cmp_to_key(none_last)))
输出:
[1, 2, 4, 9, None, None]
def cmp(a, b):
if a < b:
return -1
if a > b:
return 1
return 0
sorted_list = sorted(data, key=cmp_to_key(cmp))
多条件比较模板(按重要性排序):
def cmp(a, b):
# 一级比较
r = (a["x"] > b["x"]) - (a["x"] < b["x"])
if r != 0: return r
# 二级比较
r = (a["y"] > b["y"]) - (a["y"] < b["y"])
if r != 0: return r
# 三级比较
return (a["z"] > b["z"]) - (a["z"] < b["z"])
def cmp(a, b):
return (a.id > b.id) - (a.id < b.id) or \
(a.name > b.name) - (a.name < b.name)
def reverse_cmp(a, b):
return (a < b) - (a > b)
| 方法 | 性能 |
|---|---|
| key 排序 | ⭐⭐⭐⭐⭐ 最快 |
| cmp_to_key | ⭐⭐ 较慢 |
| 手写排序算法 | ❌ 不要 |
如果排序逻辑能用 key 函数表达,一定不要用 cmp_to_key。
from functools import cmp_to_key
def cmp(a, b):
return -1 / 0 / 1
sorted(data, key=cmp_to_key(cmp))
用途:
- 复杂排序
- 多字段比较
- 特殊规则排序
- 自定义对象比较
优点:
- 功能强,能表达任意排序逻辑
缺点:
- 比 key 排序慢
- 不能用于异步
- cmp 函数必须返回 -1/0/1