Python高级特性:sorted() 排序完全指南
灵活、高效的数据排序
前言
排序是编程中最常用的操作之一。Python 提供了内置的 sorted() 函数,可以对各种可迭代对象进行排序。与列表的 sort() 方法不同,sorted() 会返回一个新列表,原对象保持不变。
本文将系统讲解 sorted() 的基本用法、自定义排序规则、复杂对象排序、多级排序以及性能优化技巧,帮助你掌握Python中强大的排序功能。
📚 本文内容基于 :道满PythonAI - 排序教程
一、排序基础
1.1 基本语法
python
sorted(iterable, key=None, reverse=False)
| 参数 | 说明 |
|---|---|
iterable |
可迭代对象(列表、元组、字符串等) |
key |
可选,指定排序依据的函数 |
reverse |
可选,True为降序,False为升序(默认) |
| 返回值 | 新的排序后的列表 |
1.2 基本示例
python
# 数字排序
numbers = [36, 5, -12, 9, -21]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [-21, -12, 5, 9, 36]
print(numbers) # [36, 5, -12, 9, -21] 原列表不变
# 字符串排序(按ASCII码)
words = ['banana', 'apple', 'Cherry', 'date']
print(sorted(words)) # ['Cherry', 'apple', 'banana', 'date']
# 注意:大写字母排在小写字母前面(ASCII码中'A'=65, 'a'=97)
二、自定义排序:key 参数
sorted() 是一个高阶函数 ,可以接收 key 参数来自定义排序规则。key 函数会应用于每个元素,然后根据返回的结果进行排序。
2.1 按绝对值排序
python
numbers = [36, 5, -12, 9, -21]
# 按绝对值排序
sorted_by_abs = sorted(numbers, key=abs)
print(sorted_by_abs) # [5, 9, -12, -21, 36]
# 计算过程:
# abs(36)=36, abs(5)=5, abs(-12)=12, abs(9)=9, abs(-21)=21
# 按[36,5,12,9,21]排序 → [5,9,12,21,36]
# 对应原值 → [5, 9, -12, -21, 36]
2.2 按字符串长度排序
python
words = ['python', 'is', 'awesome', 'and', 'powerful']
# 按长度升序
sorted_by_len = sorted(words, key=len)
print(sorted_by_len)
# ['is', 'and', 'python', 'awesome', 'powerful']
# 按长度降序
sorted_by_len_desc = sorted(words, key=len, reverse=True)
print(sorted_by_len_desc)
# ['awesome', 'powerful', 'python', 'and', 'is']
三、字符串排序与大小写处理
3.1 默认ASCII排序的问题
python
words = ['bob', 'about', 'Zoo', 'Credit']
# 默认排序(按ASCII码)
print(sorted(words))
# ['Credit', 'Zoo', 'about', 'bob']
# 因为 'C' (67) < 'Z' (90) < 'a' (97) < 'b' (98)
3.2 忽略大小写排序
使用 str.lower 或 str.upper 作为 key 函数:
python
words = ['bob', 'about', 'Zoo', 'Credit']
# 忽略大小写排序
sorted_ignore_case = sorted(words, key=str.lower)
print(sorted_ignore_case)
# ['about', 'bob', 'Credit', 'Zoo']
# 降序
sorted_desc = sorted(words, key=str.lower, reverse=True)
print(sorted_desc)
# ['Zoo', 'Credit', 'bob', 'about']
四、复杂对象排序
4.1 对元组列表排序
python
students = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
# 方法1:定义普通函数
def by_name(t):
return t[0].lower() # 按名字(忽略大小写)
def by_score(t):
return t[1] # 按成绩
# 按名字升序
sorted_by_name = sorted(students, key=by_name)
print(sorted_by_name)
# [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
# 按成绩升序
sorted_by_score = sorted(students, key=by_score)
print(sorted_by_score)
# [('Bart', 66), ('Bob', 75), ('Lisa', 88), ('Adam', 92)]
# 按成绩降序(使用负数)
def by_score_desc(t):
return -t[1]
sorted_by_score_desc = sorted(students, key=by_score_desc)
print(sorted_by_score_desc)
# [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
4.2 对字典列表排序
python
students_dict = [
{'name': 'Bob', 'score': 75},
{'name': 'Adam', 'score': 92},
{'name': 'Bart', 'score': 66},
{'name': 'Lisa', 'score': 88}
]
# 按score排序
sorted_by_score = sorted(students_dict, key=lambda x: x['score'])
print(sorted_by_score)
# [{'name': 'Bart', 'score': 66}, {'name': 'Bob', 'score': 75},
# {'name': 'Lisa', 'score': 88}, {'name': 'Adam', 'score': 92}]
# 按name排序(忽略大小写)
sorted_by_name = sorted(students_dict, key=lambda x: x['name'].lower())
print(sorted_by_name)
# [{'name': 'Adam', 'score': 92}, {'name': 'Bart', 'score': 66},
# {'name': 'Bob', 'score': 75}, {'name': 'Lisa', 'score': 88}]
五、现代Python排序技巧
5.1 使用 lambda 表达式简化
python
students = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
# 按名字排序
sorted_by_name = sorted(students, key=lambda x: x[0].lower())
# 按成绩降序
sorted_by_score = sorted(students, key=lambda x: -x[1])
print(sorted_by_name) # [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
print(sorted_by_score) # [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
5.2 使用 operator 模块提高效率
operator 模块提供了 itemgetter 和 attrgetter,比 lambda 更快:
python
from operator import itemgetter, attrgetter
students = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
# 按名字排序
sorted_by_name = sorted(students, key=itemgetter(0))
print(sorted_by_name)
# [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
# 按成绩排序(降序)
sorted_by_score = sorted(students, key=itemgetter(1), reverse=True)
print(sorted_by_score)
# [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
# 对于对象列表,使用attrgetter
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
students_obj = [Student('Bob', 75), Student('Adam', 92), Student('Bart', 66)]
sorted_by_name = sorted(students_obj, key=attrgetter('name'))
print([s.name for s in sorted_by_name]) # ['Adam', 'Bart', 'Bob']
5.3 多级排序
先按成绩降序,成绩相同再按名字升序:
python
students = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88), ('Eve', 75)]
# 方法1:使用lambda返回元组
sorted_multi = sorted(students, key=lambda x: (-x[1], x[0].lower()))
print(sorted_multi)
# [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Eve', 75), ('Bart', 66)]
# 方法2:多次排序(Python的排序是稳定的)
# 先按名字升序,再按成绩降序
step1 = sorted(students, key=lambda x: x[0].lower()) # 按名字
step2 = sorted(step1, key=lambda x: x[1], reverse=True) # 再按成绩
print(step2)
# [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Eve', 75), ('Bart', 66)]
# 说明:稳定排序保证相同成绩时,名字的顺序保持第一次排序的结果
5.4 使用 functools.cmp_to_key(兼容旧式比较函数)
如果你有老式的比较函数(返回-1/0/1),可以转换:
python
from functools import cmp_to_key
def compare(a, b):
"""自定义比较函数:按成绩降序,成绩相同按名字升序"""
if a[1] != b[1]:
return b[1] - a[1] # 成绩高的排前面
else:
if a[0].lower() < b[0].lower():
return -1
elif a[0].lower() > b[0].lower():
return 1
return 0
students = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88), ('Eve', 75)]
sorted_custom = sorted(students, key=cmp_to_key(compare))
print(sorted_custom)
# [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Eve', 75), ('Bart', 66)]
六、实战案例
6.1 对字典按值排序
python
# 统计单词出现次数
word_counts = {'apple': 5, 'banana': 3, 'cherry': 8, 'date': 3}
# 按值升序
sorted_by_value = sorted(word_counts.items(), key=lambda x: x[1])
print(sorted_by_value)
# [('banana', 3), ('date', 3), ('apple', 5), ('cherry', 8)]
# 按值降序
sorted_by_value_desc = sorted(word_counts.items(), key=lambda x: x[1], reverse=True)
print(sorted_by_value_desc)
# [('cherry', 8), ('apple', 5), ('banana', 3), ('date', 3)]
# 先按值降序,值相同按键升序
sorted_multi = sorted(word_counts.items(), key=lambda x: (-x[1], x[0]))
print(sorted_multi)
# [('cherry', 8), ('apple', 5), ('banana', 3), ('date', 3)]
6.2 对文件列表排序
python
import os
from operator import attrgetter
# 获取当前目录下的文件信息
files = [os.path.join('.', f) for f in os.listdir('.') if os.path.isfile(f)]
file_infos = [(f, os.path.getsize(f), os.path.getmtime(f)) for f in files]
# 按文件大小排序
by_size = sorted(file_infos, key=lambda x: x[1])
print("按大小排序(最小在前):")
for name, size, _ in by_size[:3]:
print(f" {name}: {size} bytes")
# 按修改时间排序(最新在前)
by_time = sorted(file_infos, key=lambda x: x[2], reverse=True)
print("\n按修改时间排序(最新在前):")
for name, _, mtime in by_time[:3]:
print(f" {name}: {time.ctime(mtime)}")
6.3 排序并去重
python
# 方法1:使用sorted(set())
numbers = [5, 2, 8, 2, 1, 5, 9, 8]
unique_sorted = sorted(set(numbers))
print(unique_sorted) # [1, 2, 5, 8, 9]
# 方法2:使用dict.fromkeys()保持首次出现顺序(Python 3.7+)
unique_ordered = list(dict.fromkeys(numbers))
print(unique_ordered) # [5, 2, 8, 1, 9]
七、性能考虑
| 方法 | 时间复杂度 | 说明 |
|---|---|---|
sorted() |
O(n log n) | Timsort算法,稳定排序 |
list.sort() |
O(n log n) | 原地排序,更省内存 |
key函数 |
O(n) | 每个元素调用一次,缓存结果 |
性能优化建议
- key函数应尽量简单:复杂函数会拖慢排序
- 避免在key中使用lambda调用慢速函数:可以预先计算
- 对于大数据集,使用
operator.itemgetter比 lambda 快
python
import timeit
students = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] * 1000
# lambda方式
def sort_lambda():
return sorted(students, key=lambda x: x[1])
# itemgetter方式(更快)
from operator import itemgetter
def sort_itemgetter():
return sorted(students, key=itemgetter(1))
# 性能测试(在实际环境中运行)
# print(timeit.timeit(sort_lambda, number=1000))
# print(timeit.timeit(sort_itemgetter, number=1000))
八、总结
| 知识点 | 要点 |
|---|---|
| 基本排序 | sorted(iterable) 返回新列表 |
| 自定义key | key 参数指定排序依据函数 |
| 降序 | reverse=True |
| 忽略大小写 | key=str.lower |
| 元组/字典排序 | key=lambda x: x[0] 或 itemgetter |
| 多级排序 | key=lambda x: (-x[1], x[0]) |
| 稳定排序 | 相等元素保持原顺序 |
| 性能优化 | 使用 itemgetter,简化key函数 |
核心要点:
- ✅
sorted()返回新列表,原对象不变 - ✅
key参数是高阶函数的核心,可以自定义排序规则 - ✅ 字符串排序注意大小写,使用
str.lower忽略大小写 - ✅ 多级排序通过返回元组实现
- ✅
operator.itemgetter比 lambda 更高效 - ✅ Python的排序是稳定的,可以链式排序实现复杂逻辑
掌握 sorted() 函数,可以让你轻松应对各种排序需求,写出简洁高效的代码。
📚 相关推荐阅读
💡 Python 学习不走弯路!
体系化实战路线:基础语法 · 异步Web开发 · 数据采集 · 计算机视觉 · NLP · 大模型RAG实战
------ 全在 「道满PythonAI」!
如果这篇文章对你有帮助,欢迎点赞、评论、收藏,你的支持是我持续分享的动力!🎉