查看: 299|回复: 3

Python sorted自定义排序 key参数与多级排序实战

[复制链接]
发表于 19 小时前 | 显示全部楼层 |阅读模式
在Python编程中,排序是数据处理的常用操作。Python提供了两个排序工具:list.sort()(原地排序,直接修改原列表)和sorted()(返回新排序列表,不修改原数据)。其中sorted()功能更灵活,能对任意可迭代对象进行排序,并通过key参数实现自定义排序规则。
  1. >>> numbers = [3, 1, 4, 1, 5, 9, 2, 6]
  2. >>> sorted_numbers = sorted(numbers)
  3. >>> sorted_numbers
  4. [1, 1, 2, 3, 4, 5, 6, 9]
  5. >>> numbers  # 原列表不变
  6. [3, 1, 4, 1, 5, 9, 2, 6]
复制代码

list.sort()原地修改,而sorted()返回新列表,可作用于任何可迭代对象。

一、sorted()基本参数
sorted(iterable, key=None, reverse=False)。reverse控制升降序:
  1. >>> sorted([3,1,4,1,5], reverse=True)
  2. [5, 4, 3, 1, 1]
复制代码

key参数是排序的灵魂——接收一个函数,对每个元素返回一个用于比较的值。原始元素不变,排序依据key的返回值。
  1. >>> words = ["banana", "apple", "Cherry", "date", "Elderberry"]
  2. >>> sorted(words, key=str.lower)  # 忽略大小写
  3. ['apple', 'banana', 'Cherry', 'date', 'Elderberry']
  4. >>> sorted(words, key=len)
  5. ['date', 'apple', 'banana', 'Cherry', 'Elderberry']
  6. >>> import re
  7. >>> codes = ["item2", "item10", "item1", "item20"]
  8. >>> def extract_number(s):
  9. ...     match = re.search(r'\d+', s)
  10. ...     return int(match.group()) if match else 0
  11. >>> sorted(codes, key=extract_number)
  12. ['item1', 'item2', 'item10', 'item20']
复制代码

二、lambda作为key——最常用的排序方式
对字典列表按字段排序:
  1. >>> employees = [
  2. ...     {"name": "张三", "age": 28, "salary": 15000, "department": "技术部"},
  3. ...     {"name": "李四", "age": 35, "salary": 20000, "department": "管理部"},
  4. ...     {"name": "王五", "age": 22, "salary": 12000, "department": "技术部"},
  5. ...     {"name": "赵六", "age": 30, "salary": 18000, "department": "市场部"},
  6. ...     {"name": "钱七", "age": 25, "salary": 13000, "department": "技术部"},
  7. ... ]
  8. >>> sorted(employees, key=lambda e: e["age"])
复制代码

多级排序:key返回元组,Python按元组元素依次比较。降序数字字段可用负号。
  1. >>> sorted(employees, key=lambda e: (e["department"], -e["salary"]))
复制代码

注意:对非数字字段的降序不能直接用负号,可借助排序稳定性,先按次要key升序排,再按主要key降序排(反向两次sorted)。

三、使用operator模块
itemgetter和attrgetter比lambda更简洁高效:
  1. >>> from operator import itemgetter, attrgetter
  2. >>> sorted(employees, key=itemgetter("department", "salary"))
  3. >>> class Student:
  4. ...     def __init__(self, name, score):
  5. ...         self.name = name
  6. ...         self.score = score
  7. >>> students = [Student("Alice",85), Student("Bob",92), Student("Charlie",78)]
  8. >>> sorted(students, key=attrgetter("score"), reverse=True)
复制代码

四、自定义对象的排序
方法一:在类中实现__lt__(小于)方法,即可直接使用sorted()。
  1. >>> from functools import total_ordering
  2. >>> class Task:
  3. ...     def __init__(self, name, priority, due_date):
  4. ...         self.name = name
  5. ...         self.priority = priority
  6. ...         self.due_date = due_date
  7. ...     def __lt__(self, other):
  8. ...         if self.priority != other.priority:
  9. ...             return self.priority < other.priority
  10. ...         return self.due_date < other.due_date
  11. ...     def __repr__(self):
  12. ...         return f"Task({self.name}, pri={self.priority}, due={self.due_date})"
  13. >>> sorted(tasks)
复制代码

方法二:使用key lambda,无需修改类定义:
  1. >>> sorted(tasks, key=lambda t: (t.priority, t.due_date))
复制代码

五、实战案例
1. 日志按时间排序
从日志行中提取时间戳,作为key。若需按时间后再按日志级别排序,可返回元组。
  1. >>> logs = [
  2. ...     "[2024-03-15 14:30:22] ERROR 数据库连接失败",
  3. ...     "[2024-03-15 14:25:10] INFO 服务器启动",
  4. ...     "[2024-03-15 14:32:05] WARNING 内存使用率85%",
  5. ...     "[2024-03-15 14:25:10] DEBUG 加载配置文件",
  6. ...     "[2024-03-15 14:28:45] INFO 用户登录成功",
  7. ... ]
  8. >>> def extract_timestamp(log_line):
  9. ...     return log_line[1:20]
  10. >>> LEVEL_PRIORITY = {"DEBUG": 0, "INFO": 1, "WARNING": 2, "ERROR": 3}
  11. >>> def sort_key(log_line):
  12. ...     ts = extract_timestamp(log_line)
  13. ...     level = log_line[22:].split()[0]
  14. ...     return (ts, LEVEL_PRIORITY.get(level, 99))
  15. >>> sorted(logs, key=sort_key)
复制代码

2. 自然排序
人类期望的排序如file1, file2, ..., file10,而非字典序的file1, file10, file11, file2。通过正则拆分数字和文本。
  1. >>> import re
  2. >>> def natural_sort_key(s):
  3. ...     parts = re.split(r'(\d+)', s)
  4. ...     return [int(part) if part.isdigit() else part.lower() for part in parts]
  5. >>> files = ["file10.txt", "file1.txt", "file2.txt", "file20.txt", "file11.txt"]
  6. >>> sorted(files, key=natural_sort_key)
  7. ['file1.txt', 'file2.txt', 'file10.txt', 'file11.txt', 'file20.txt']
复制代码

六、总结
sorted()的key参数是排序核心,通过返回不同比较值实现任意规则排序。元组实现多级排序,利用稳定性可构造复杂排序。lambda和operator模块是常用工具。掌握这些,就能轻松应对各种排序需求。
回复

使用道具 举报

发表于 13 小时前 | 显示全部楼层

Re: Python sorted自定义排序 key参数与多级排序实战

讲解得非常清晰,特别是多级排序中利用元组和负号实现数字降序的技巧很实用。另外补充一点,如果排序逻辑特别复杂(比如需要交叉比较多个字段),还可以考虑用 `functools.cmp_to_key` 把旧式的比较函数转换成 key 函数,不过大多数场景下你提到的 `lambda` 或 `operator` 模块已经足够高效了。
回复 支持 反对

使用道具 举报

发表于 13 小时前 | 显示全部楼层

Re: Python sorted自定义排序 key参数与多级排序实战

感谢分享,这篇关于 sorted 和 key 参数的讲解非常实用,尤其是多级排序用元组和负号处理数字降序的例子,正好解决了最近在处理员工数据时遇到的排序问题。之前一直用 lambda 写多层嵌套,看到用 operator.itemgetter 简洁多了。也学到了对非数字字段降序利用排序稳定性的思路,之前没注意到这里,不过实操时可能要多跑两次 sorted 有点绕。期待楼主后续能再深入讲讲 functools.cmp_to_key 或者更复杂的自定义比较场景。
回复 支持 反对

使用道具 举报

发表于 13 小时前 | 显示全部楼层

Re: Python sorted自定义排序 key参数与多级排序实战

感谢楼主的详细分享!这篇教程把 `sorted()` 和 `key` 参数讲得特别清楚,尤其是多级排序和 `itemgetter` 的对比,非常实用。我之前一直用 `lambda` 做双字段排序,没想到 `operator.itemgetter` 能直接传多个键,代码简洁多了。你提到的利用排序稳定性反向两次实现非数字降序的思路也很有启发,回头去试试。期待更多这样的实战技巧!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-20 21:57 , Processed in 0.026950 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部