使用比较函数多键排序:如何仅对单个键进行逆序排序?
Python 3 实现多属性独立升降序排序(非数值属性)
核心解决思路:用functools.cmp_to_key兼容Python 2风格比较函数
Python 3 确实移除了sort()直接传入比较函数的接口,但可以借助functools模块的cmp_to_key工具,把Python 2风格的自定义比较函数转换成sort()能识别的key参数,以此实现对每个属性单独控制升降序的需求,完全适配非数值类型属性。
示例代码演示
假设我们有一个Person类,包含name(字符串)、status(枚举类型,比如Status.ACTIVE/Status.INACTIVE)两个非数值属性,需求是:按status降序排列,status相同时按name升序排列。
from functools import cmp_to_key from enum import Enum class Status(Enum): ACTIVE = "active" INACTIVE = "inactive" class Person: def __init__(self, name, status): self.name = name self.status = status def __repr__(self): return f"Person(name='{self.name}', status={self.status.name})" # 自定义比较函数,逻辑和Python 2一致 def compare_persons(a, b): # 先处理status的降序逻辑 if a.status != b.status: # 按业务规则,ACTIVE优先级高于INACTIVE,降序时让ACTIVE排在前面 return -1 if a.status == Status.ACTIVE else 1 # status相同时,处理name的升序逻辑 if a.name < b.name: return -1 elif a.name > b.name: return 1 else: return 0 # 测试数据 people = [ Person("Bob", Status.INACTIVE), Person("Alice", Status.ACTIVE), Person("Charlie", Status.ACTIVE), Person("David", Status.INACTIVE) ] # 用cmp_to_key转换比较函数后传入sort people.sort(key=cmp_to_key(compare_persons)) print(people) # 输出结果: [Person(name='Alice', status=ACTIVE), Person(name='Charlie', status=ACTIVE), Person(name='Bob', status=INACTIVE), Person(name='David', status=INACTIVE)]
关键说明
- 自定义比较函数的逻辑和Python 2完全一致:返回
-1表示a应排在b前面,返回1表示b应排在a前面,返回0表示两者排序优先级相同。 - 针对非数值属性,可直接根据业务规则定义比较逻辑,比如枚举值的优先级、字符串的字典序等,无需依赖取反操作。
cmp_to_key的性能略低于直接使用key参数,但对于绝大多数业务场景,这个性能差异可以忽略不计。
备选方案:为非数值属性映射可排序键
如果不想使用比较函数,也可以为非数值属性映射一个可排序的量化值,比如给status分配权重:
def get_sort_key(person): # 给status分配权重,ACTIVE权重2、INACTIVE权重1,用负权重实现降序 status_weight = 2 if person.status == Status.ACTIVE else 1 # 元组排序时会依次比较元素,这里先按负权重降序,再按name升序 return (-status_weight, person.name) people.sort(key=get_sort_key)
这种方式性能更优,但需要为每个非数值属性设计合理的映射规则,适合排序规则固定且容易量化的场景。
内容的提问来源于stack exchange,提问作者Hays Jedi
相关产品推荐
相关产品推荐

