Python函数sorted()排序函数与自定义排序规则一、开篇排序——数据处理的基本功排序是编程中最常见的操作之一。Python提供了两个排序工具list.sort()原地排序和sorted()返回新排序结果。其中sorted()更灵活、更通用——它能排序任何可迭代对象还能自定义排序规则。⌨️ 先看最基础的用法# sorted()的基本用法numbers[3,1,4,1,5,9,2,6]sorted_numberssorted(numbers)print(sorted_numbers)# [1, 1, 2, 3, 4, 5, 6, 9]print(numbers)# [3, 1, 4, 1, 5, 9, 2, 6] ← 原列表不变# list.sort()——原地排序直接修改原列表numbers.sort()print(numbers)# [1, 1, 2, 3, 4, 5, 6, 9] ← 被修改了# 关键区别# sorted(iterable) → 返回新列表原数据不变可用于任何可迭代对象# list.sort() → 原地排序不返回新列表只能用于列表sorted()的核心优势在于它的key参数——让你能按任意规则排序这个参数是Python排序系统的灵魂。二、sorted()的基本参数2.1 三个参数详解# sorted(iterable, keyNone, reverseFalse)# iterable: 要排序的可迭代对象# key: 排序依据的函数接收每个元素返回用于比较的值# reverse: 是否降序True降序False升序# reverse参数——升降序控制numbers[3,1,4,1,5]print(sorted(numbers))# [1, 1, 3, 4, 5] 升序print(sorted(numbers,reverseTrue))# [5, 4, 3, 1, 1] 降序# key参数——自定义排序依据words[banana,apple,Cherry,date,Elderberry]# 默认排序——按Unicode码点大写字母在小写字母前print(sorted(words))# [Cherry, Elderberry, apple, banana, date]# 按字母顺序忽略大小写print(sorted(words,keystr.lower))# [apple, banana, Cherry, date, Elderberry]# 按单词长度排序print(sorted(words,keylen))# [date, apple, banana, Cherry, Elderberry]2.2 key参数的详解# key函数的规则# 1. key接收一个参数序列中的每个元素# 2. key返回一个用于比较的值# 3. 排序根据key的返回值进行不是原始元素# 4. 原始元素不变——只是排序的依据变了# 绝对值排序numbers[-5,3,-1,4,-2]print(sorted(numbers,keyabs))# [-1, -2, 3, 4, -5]# 按字符串中数字的大小排序codes[item2,item10,item1,item20]print(sorted(codes))# [item1, item10, item2, item20] ← 字典序item10在item2前importredefextract_number(s):提取字符串中的数字matchre.search(r\d,s)returnint(match.group())ifmatchelse0print(sorted(codes,keyextract_number))# [item1, item2, item10, item20] ← 按数字大小排# 按时间排序times[2024-03-15,2023-12-01,2024-01-20,2023-06-10]print(sorted(times))# 字符串比较恰好对日期格式有效# [2023-06-10, 2023-12-01, 2024-01-20, 2024-03-15]三、lambda作为key——最常用的排序方式3.1 按字典字段排序# ⌨️ 排序字典列表——职场中最常用的技能employees[{name:张三,age:28,salary:15000,department:技术部},{name:李四,age:35,salary:20000,department:管理部},{name:王五,age:22,salary:12000,department:技术部},{name:赵六,age:30,salary:18000,department:市场部},{name:钱七,age:25,salary:13000,department:技术部},]# 按年龄排序by_agesorted(employees,keylambdae:e[age])print(按年龄,[e[name]foreinby_age])# [王五, 钱七, 张三, 赵六, 李四]# 按薪资降序by_salarysorted(employees,keylambdae:e[salary],reverseTrue)print(按薪资降序,[(e[name],e[salary])foreinby_salary])# [(李四, 20000), (赵六, 18000), (张三, 15000), (钱七, 13000), (王五, 12000)]# 按年龄排序同年龄按薪资降序多级排序by_age_salarysorted(employees,keylambdae:(e[age],-e[salary]))print(年龄薪资,[(e[name],e[age],e[salary])foreinby_age_salary])3.2 多级排序# 多级排序key返回一个元组# Python按元组的元素顺序依次比较# 先按部门再按薪资降序resultssorted(employees,keylambdae:(e[department],-e[salary]))foreinresults:print(f{e[department]}-{e[name]}: ¥{e[salary]})# 管理部 - 李四: ¥20000# 技术部 - 张三: ¥15000# 技术部 - 钱七: ¥13000# 技术部 - 王五: ¥12000# 市场部 - 赵六: ¥18000# 技巧降序字段用负号# 数字降序-e[salary]注意这只对数字有效# 字符串降序没法用负号需要分两次排序或用functools.cmp_to_key# 多级排序的另一种方法多次sorted()利用稳定性# Python的排序是稳定的——相同key的元素保持原来的相对顺序# 先按次要key排再按主要key排resultsorted(employees,keylambdae:e[salary],reverseTrue)resultsorted(result,keylambdae:e[department])# 结果同一部门内按薪资降序四、高级排序技巧4.1 使用operator模块fromoperatorimportitemgetter,attrgetter,methodcaller# itemgetter——获取字典的指定字段相当于lambda e: e[key]# 比lambda稍快一些而且更简洁by_departmentsorted(employees,keyitemgetter(department))print([e[department]foreinby_department])# itemgetter多级排序by_dept_salarysorted(employees,keyitemgetter(department,salary))# attrgetter——获取对象的属性classStudent:def__init__(self,name,score):self.namename self.scorescore students[Student(Alice,85),Student(Bob,92),Student(Charlie,78),]by_scoresorted(students,keyattrgetter(score),reverseTrue)forsinby_score:print(f{s.name}:{s.score})4.2 自定义对象的排序fromfunctoolsimporttotal_ordering# 方法一实现比较方法classTask:def__init__(self,name,priority,due_date):self.namename self.prioritypriority self.due_datedue_datedef__lt__(self,other):小于比较先按priority再按due_dateifself.priority!other.priority:returnself.priorityother.priorityreturnself.due_dateother.due_datedef__repr__(self):returnfTask({self.name}, pri{self.priority}, due{self.due_date})tasks[Task(写报告,2,2024-06-15),Task(修复Bug,1,2024-06-10),Task(开会,2,2024-06-12),Task(代码审查,1,2024-06-08),]print(sorted(tasks))# [Task(代码审查, pri1, due2024-06-08), Task(修复Bug, pri1, due2024-06-10),# Task(开会, pri2, due2024-06-12), Task(写报告, pri2, due2024-06-15)]# 方法二使用key更灵活不修改类tasks_sortedsorted(tasks,keylambdat:(t.priority,t.due_date))五、实战案例5.1 日志按时间排序# 场景排序杂乱的日志条目logs[[2024-03-15 14:30:22] ERROR 数据库连接失败,[2024-03-15 14:25:10] INFO 服务器启动,[2024-03-15 14:32:05] WARNING 内存使用率85%,[2024-03-15 14:25:10] DEBUG 加载配置文件,[2024-03-15 14:28:45] INFO 用户登录成功,]defextract_timestamp(log_line):从日志行中提取时间戳returnlog_line[1:20]# 提取 [2024-03-15 14:30:22] 中的时间sorted_logssorted(logs,keyextract_timestamp)forloginsorted_logs:print(log)# 按时间正序排列# 对于同时间的日志按级别排序LEVEL_PRIORITY{DEBUG:0,INFO:1,WARNING:2,ERROR:3}defsort_key(log_line):timestampextract_timestamp(log_line)# 提取日志级别levellog_line[22:].split()[0]priorityLEVEL_PRIORITY.get(level,99)return(timestamp,priority)sorted_logssorted(logs,keysort_key)5.2 自然排序importre# 人类期望的排序自然排序file1, file2, ..., file10, file11# 而不是字典序file1, file10, file11, file2, ...defnatural_sort_key(s):自然排序的key函数# 将字符串分割为文本和数字的交替序列partsre.split(r(\d),s)# 将数字部分转为整数return[int(part)ifpart.isdigit()elsepart.lower()forpartinparts]files[file10.txt,file1.txt,file2.txt,file20.txt,file11.txt]print(sorted(files))# [file1.txt, file10.txt, file11.txt, file2.txt, file20.txt] ← 字典序print(sorted(files,keynatural_sort_key))# [file1.txt, file2.txt, file10.txt, file11.txt, file20.txt] ← 自然序六、总结sorted()的key参数是Python排序系统的精髓它让你能按任意规则排序——从简单的按长度排到复杂的多字段组合排序。核心要点sorted()返回新列表list.sort()原地排序key函数定义排序依据——返回什么就按什么排元组key实现多级排序——keylambda x: (x.a, -x.b)稳定性——两次sorted可实现复杂排序lambda key是最常用的排序方式✅记住key返回的不是元素本身而是用于比较的排序值。合理使用key你能排序任何数据结构。