Python 系列(十六) sort() 和 sorted() 详解,掌握数据排序技巧
1 | 作者:李晓辉 |
- Python 系列(一) 从零开始认识 Python,入门其实没那么难
- Python 系列(二) 学 Python 的第一步,把开发环境搭建好
- Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
- Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
- Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
- Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
- Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
- Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
- Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
- Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
- Python 系列(十一) list、tuple、set、dict,到底该怎么选?
- Python 系列(十二) List 列表实战,搞定数据增删改查与批量处理
- Python 系列(十三) Tuple 元组实战,搞懂不可变特性与 enumerate() 遍历
- Python 系列(十四) Set 集合实战,搞定数据去重与集合运算
- Python 系列(十五) Dict 字典详解,掌握快速查找与键值操作
- Python 系列(十六) sort() 和 sorted() 详解,掌握数据排序技巧
- Python 系列(十七) 自定义函数入门,让重复代码一次编写多次使用
- Python 系列(十八) 函数参数详解,搞懂位置参数、关键字参数和默认参数
- Python 系列(十九) 从 *args 到 **kwargs,让函数调用更灵活
前面我们学习了 Python 中的 List 列表、Tuple 元组、Set 集合和 Dict 字典。
List 可以保存一组有序数据,Tuple 适合保存不希望被修改的数据,Set 擅长去重和集合运算,Dict 则可以通过键快速查找对应的值。
不过,在实际工作中,数据保存好了还不够,我们经常还需要对数据进行整理。
比如,监控系统收集了几十台 Linux 服务器的 CPU 使用率,我们希望按照使用率从高到低排列,快速找到最需要关注的服务器;又或者,我们需要按照磁盘使用率排序,优先处理空间不足的机器。
这些场景都离不开排序。
Python 提供了
sort()和sorted()两种常用的排序方式。它们看起来很相似,但使用方法和适用场景并不完全相同。这一篇,我们就把排序操作和前面学过的数据结构结合起来,看看不同类型的数据应该如何排序,以及实际开发中应该怎样选择合适的数据结构。
为什么需要排序?
先看一个简单的例子。
假设我们收集到了几台服务器的 CPU 使用率:
1 | cpu_usage = [35, 82, 47, 96, 61] |
运行结果:
1 | [35, 82, 47, 96, 61] |
这些数据记录了服务器的 CPU 使用率,但排列顺序比较随意。如果我们想快速找出 CPU 使用率最高的服务器,就需要逐个比较。
如果先对数据进行排序,情况就简单多了:
1 | cpu_usage = [35, 82, 47, 96, 61] |
运行结果:
1 | [96, 82, 61, 47, 35] |
现在,CPU 使用率最高的数据就出现在列表最前面了。
当然,这里只有几个数字,手动查看也很容易。但如果监控系统收集了几百台甚至几千台服务器的数据,排序就能帮助我们更高效地整理信息。
在学习具体用法之前,先记住 Python 排序的两个核心工具:sort() 和 sorted()。
sort() 和 sorted() 有什么区别?
虽然这两个方法都可以排序,但它们的工作方式不同。
使用 sort() 原地排序
sort() 是列表对象的方法,只能直接用于 List。
它会修改原列表,而不是创建一个新的排序列表。
1 | numbers = [35, 82, 47, 96, 61] |
运行结果:
1 | [35, 47, 61, 82, 96] |
默认情况下,sort() 按照升序排列,也就是从小到大。
如果想按照降序排列,可以设置 reverse=True:
1 | numbers = [35, 82, 47, 96, 61] |
运行结果:
1 | [96, 82, 61, 47, 35] |
这里需要特别注意:sort() 会直接修改原列表,并且它的返回值是 None。
例如,下面这种写法就是错误的:
1 | numbers = [3, 1, 2] |
运行结果:
1 | None |
为什么会这样?
因为 sort() 已经在原列表上完成了排序,但它并不返回排序后的列表。如果把它的返回值重新赋给 numbers,原本的列表引用就会被 None 替换。
所以,使用 sort() 时,直接调用即可:
1 | numbers.sort() |
使用 sorted() 返回新列表
与 sort() 不同,sorted() 是 Python 的内置函数,可以接收多种可迭代对象,例如 List、Tuple、Set、Dict 等。
它会返回一个新的列表,而不会直接修改原来的数据。
1 | numbers = [35, 82, 47, 96, 61] |
运行结果:
1 | 原列表: [35, 82, 47, 96, 61] |
可以看到,原列表的顺序保持不变,排序结果则保存在新列表中。
同样,我们可以使用 reverse=True 实现降序排序:
1 | numbers = [35, 82, 47, 96, 61] |
运行结果:
1 | [96, 82, 61, 47, 35] |
那么,什么时候使用哪一种?
如果只需要调整原列表的顺序,可以使用 sort();如果需要保留原始数据,同时得到排序结果,则使用 sorted()。
List 列表排序
List 是最常见的排序对象,因为它本身就是一种有序、可变的数据结构。
除了数字,我们还可以对字符串列表进行排序。
对字符串进行排序
例如,我们保存了几台服务器的名称:
1 | servers = ["web-03", "web-01", "db-01", "web-02"] |
运行结果:
1 | ['db-01', 'web-01', 'web-02', 'web-03'] |
默认情况下,字符串会按照 Unicode 码点顺序进行比较。
对于这里的服务器名称,这样的排序方式通常比较直观。但如果字符串中包含大小写、中文或数字编号,就需要根据实际需求考虑排序规则。
使用 reverse() 反转列表
列表还提供了 reverse() 方法,用于直接反转当前列表中的元素顺序。
1 | numbers = [1, 2, 3, 4, 5] |
运行结果:
1 | [5, 4, 3, 2, 1] |
注意,reverse() 和 sort(reverse=True) 并不是一回事。
reverse() 只会反转当前顺序,不会比较元素大小;sort(reverse=True) 则会先按照排序规则排列,再以降序方式组织结果。
例如:
1 | numbers = [3, 1, 5, 2, 4] |
运行结果:
1 | [4, 2, 5, 1, 3] |
这两个方法的用途不同,不要混淆。
Tuple 元组排序
前面我们学习过,Tuple 是不可变的数据结构。创建元组后,不能像列表一样直接修改其中的元素。
那么,如果需要对元组进行排序,应该怎么办?
答案是使用 sorted()。
1 | numbers = (3, 1, 2) |
运行结果:
1 | [1, 2, 3] |
这里有一个细节:虽然输入的是 Tuple,但 sorted() 返回的仍然是 List。
如果希望最终结果依然是元组,可以再使用 tuple() 转换:
1 | numbers = (3, 1, 2) |
运行结果:
1 | (1, 2, 3) |
不过,如果没有必须保留元组类型的需求,直接使用 sorted() 的返回结果就可以了。
毕竟,排序操作本身并不会让元组变得可变,我们只是创建了一个新的排序结果。
Set 集合排序
Set 集合有一个重要特点:元素唯一,不保证按照插入顺序保存元素。
例如:
1 | numbers = {5, 1, 3, 2, 4} |
集合的输出顺序不应该被当作固定顺序使用。
如果希望按照从小到大的顺序查看集合中的元素,可以使用 sorted():
1 | numbers = {5, 1, 3, 2, 4} |
运行结果:
1 | [1, 2, 3, 4, 5] |
需要注意,sorted() 返回的是列表,而不是集合。
这其实很合理:集合本身不负责维护排序后的顺序,而列表可以按照指定顺序保存元素。
如果只是临时查看排序结果,直接使用 sorted() 就足够了。
如果后续业务还需要集合的去重和集合运算能力,就可以继续保留原始 Set,而不必为了排序把它替换掉。
Dict 字典排序
字典与 List、Tuple、Set 有一个重要区别:它保存的是键值对。
在现代 Python 中,字典会保留键值对的插入顺序,但这并不意味着字典会自动按照键或值进行排序。
如果需要对字典中的数据排序,就要先明确:我们到底是想按照键排序,还是按照值排序?
按照键排序
例如,我们保存了不同服务器的 CPU 使用率:
1 | cpu_usage = { |
运行结果:
1 | ['db-01', 'web-01', 'web-03'] |
直接对字典使用 sorted(),默认遍历并排序的是字典的键。
因此,这段代码得到的是一个按照键排序的列表,而不是一个新的字典。
如果希望得到按照键排序的字典,可以使用 sorted() 获取排序后的键值对,再通过 dict() 创建字典:
1 | cpu_usage = { |
运行结果:
1 | {'db-01': 61, 'web-01': 35, 'web-03': 82} |
这个新字典会按照排序后的顺序插入键值对,因此在后续遍历时也会保持这个顺序。
按照值排序
如果我们更关心的是哪台服务器的 CPU 使用率最高,就应该按照值排序。
这时可以使用 items() 获取键值对,再通过 key 参数指定排序依据:
1 | cpu_usage = { |
运行结果:
1 | [('web-01', 35), ('db-01', 61), ('web-03', 82)] |
这里的 item 是一个包含键和值的元组。
例如,("web-03", 82) 中,item[0] 表示服务器名称,item[1] 表示 CPU 使用率。
key=lambda item: item[1] 的意思是:按照每个键值对中的第二个元素,也就是 CPU 使用率进行排序。
如果想把 CPU 使用率最高的服务器排在最前面,可以加上 reverse=True:
1 | sorted_cpu = sorted( |
运行结果:
1 | [('web-03', 82), ('db-01', 61), ('web-01', 35)] |
这样,我们就能快速找出 CPU 使用率最高的服务器。
需要注意,sorted() 返回的是包含元组的列表。如果想将结果重新转换成字典,也可以使用 dict(sorted_cpu)。
实战:按照 CPU 使用率找出高负载服务器
前面我们分别学习了不同数据结构的排序方法,现在把它们组合起来,完成一个更贴近实际运维工作的例子。
假设我们通过监控系统获取了多台服务器的 CPU 使用率:
1 | servers = { |
运行结果:
1 | CPU 使用率从高到低: |
现在,CPU 使用率最高的服务器已经排在最前面。
如果我们只想找出 CPU 使用率超过 80% 的服务器,还可以在排序结果上继续筛选:
1 | for name, usage in sorted_servers: |
运行结果:
1 | 需要关注: web-02, CPU 使用率: 92% |
这个例子展示了字典、排序、循环和条件判断如何配合使用。
实际工作中,CPU 使用率通常由监控系统或采集程序实时获取。我们可以将采集到的数据组织成字典,再通过排序和筛选生成巡检报告。
当然,CPU 使用率高并不一定代表服务器发生故障,还需要结合业务负载、持续时间和其他监控指标进行判断。排序的作用,是帮助我们优先发现值得关注的对象。
key 参数还能做什么?
前面我们使用了 key=lambda item: item[1],指定按照字典值进行排序。
实际上,key 参数并不局限于数字,也可以用于指定字符串长度、时间字段或者其他排序规则。
例如,我们想按照服务器名称的长度排序:
1 | servers = ["web-01", "database-01", "api-01", "cache-01"] |
运行结果:
1 | ['web-01', 'api-01', 'cache-01', 'database-01'] |
这里的 key=len 表示按照字符串长度进行排序。
如果两个服务器名称的长度相同,Python 的排序是稳定的,会保留它们在原始可迭代对象中的相对顺序。
这也意味着,我们可以根据业务需要定义自己的排序规则,而不必局限于数字大小或字符串本身的顺序。
排序操作速查
把前面的内容整理一下:
| 操作 | 适用对象 | 是否修改原数据 | 返回结果 |
|---|---|---|---|
list.sort() | List | 是 | None |
sorted(data) | 多种可迭代对象 | 否 | 新的 List |
list.reverse() | List | 是 | None |
sorted(data, reverse=True) | 多种可迭代对象 | 否 | 降序排列的新 List |
sorted(dict.items(), key=...) | Dict 的键值对 | 否 | 排序后的键值对列表 |
实际选择时,可以记住以下原则:
- 需要直接修改列表顺序时,使用
sort()。 - 需要保留原始数据时,使用
sorted()。 - 只想反转现有列表顺序时,使用
reverse()。 - 需要按照特定字段排序时,使用
key参数。 - 排序 Set、Tuple 或 Dict 时,要记住
sorted()返回的是列表。
写在最后
这一篇,我们学习了 Python 的排序操作,掌握了 sort()、sorted() 和 reverse() 的区别,也了解了 List、Tuple、Set 和 Dict 分别应该如何排序。
回顾前面的内容,我们已经逐步认识了 Python 中常见的数据结构:
List 用来保存有序数据,Tuple 用来保存不可变序列,Set 用来处理不重复元素和集合运算,Dict 用来管理键值对应关系,而排序操作则帮助我们按照业务需要整理这些数据。
在 Linux 运维自动化中,这些知识经常会组合使用。比如,使用 Dict 保存服务器信息,再根据 CPU 或磁盘使用率进行排序,最后筛选出需要优先处理的服务器。
到这里,Python 常见数据结构的基础知识就有了一个比较完整的框架。
