Python怎样实现数据排序?sorted函数进阶

絕刀狂花
发布: 2025-08-15 22:57:01
原创
889人浏览过

sorted() 函数通过 key 参数实现自定义排序规则,1. 基本用法支持升序和 reverse=true 实现降序;2. key 参数传入函数以决定排序依据,如 len 或 lambda 表达式;3. 复杂数据可用 lambda 获取字典键或对象属性;4. operator 模块的 itemgetter 和 attrgetter 提供更高效、清晰的替代方案;5. python 排序是稳定的,相等元素保持原有顺序;6. sorted() 返回新列表不修改原数据,list.sort() 则原地修改。该机制结合 timsort 算法保证 o(n log n) 性能,适用于大多数排序场景。

Python怎样实现数据排序?sorted函数进阶

sorted()
登录后复制
函数是 Python 里一个非常实用的工具,它能帮你把各种可迭代对象(比如列表、元组,甚至字符串)整理得井井有条,而且它很“体贴”,总会给你一个新的已排序列表,不会动你原来的数据。说到底,它的进阶用法,其实就是如何巧妙地利用它的
key
登录后复制
参数,去定义我们自己的排序规则,让排序不再仅仅是数字大小或字母顺序那么简单。

解决方案

Python 的

sorted()
登录后复制
函数,基本用法非常直观。你给它一个可迭代对象,它就返回一个排序好的新列表。

# 最基础的用法
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_numbers = sorted(numbers)
print(f"默认排序:{sorted_numbers}") # 输出:默认排序:[1, 1, 2, 3, 4, 5, 6, 9]

# 想要降序?加个 reverse=True 就行
sorted_numbers_desc = sorted(numbers, reverse=True)
print(f"降序排列:{sorted_numbers_desc}") # 输出:降序排列:[9, 6, 5, 4, 3, 2, 1, 1]
登录后复制

真正的魔法在于

key
登录后复制
参数。这个参数接受一个函数,
sorted()
登录后复制
会对可迭代对象中的每个元素都应用这个函数,然后根据函数返回的结果进行排序。这意味着你可以根据元素的任何“特征”来排序,而不是元素本身。

立即学习Python免费学习笔记(深入)”;

# 比如,我想根据字符串的长度来排序
words = ["apple", "banana", "cat", "dog", "elephant"]
sorted_by_length = sorted(words, key=len)
print(f"按长度排序:{sorted_by_length}") # 输出:按长度排序:['cat', 'dog', 'apple', 'banana', 'elephant']

# 再来一个,列表里是元组,我想根据元组的第二个元素来排序
data = [("apple", 5), ("banana", 2), ("cherry", 8), ("date", 1)]
# 这里我们用 lambda 表达式定义一个匿名函数,它接受一个元组 x,返回 x[1]
sorted_by_second_element = sorted(data, key=lambda x: x[1])
print(f"按元组第二个元素排序:{sorted_by_second_element}") # 输出:按元组第二个元素排序:[('date', 1), ('banana', 2), ('apple', 5), ('cherry', 8)]
登录后复制

key
登录后复制
参数到底怎么用?实现复杂数据排序的关键

当数据结构变得复杂,比如列表里装着字典,或者自定义的对象时,

key
登录后复制
参数就成了你的“瑞士军刀”。它允许你深入到每个元素的内部,抽取某个特定的值作为排序依据。

我们经常会遇到这样的场景:有一堆用户数据,每个用户都是一个字典,我们想按年龄排序。

users = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35},
    {"name": "David", "age": 25}
]

# 想要按年龄排序,年龄相同再按名字排序(这个后面会讲到)
# 这里用 lambda x: x['age'] 来告诉 sorted 函数,去拿每个字典里的 'age' 值来比
sorted_users_by_age = sorted(users, key=lambda user: user['age'])
print(f"按年龄排序的用户:{sorted_users_by_age}")
# 输出:按年龄排序的用户:[{'name': 'Bob', 'age': 25}, {'name': 'David', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Charlie', 'age': 35}]
登录后复制

这很直接,对吧?但有时候,我们的数据是自定义的类实例,比如一个

Product
登录后复制
对象,我们想按价格排序。

class Product:
    def __init__(self, name, price, stock):
        self.name = name
        self.price = price
        self.stock = stock

    def __repr__(self): # 为了方便打印,让对象看起来更直观
        return f"Product(name='{self.name}', price={self.price}, stock={self.stock})"

products = [
    Product("Laptop", 1200, 50),
    Product("Mouse", 25, 200),
    Product("Keyboard", 75, 100),
    Product("Monitor", 300, 30)
]

# 按价格排序
# lambda product: product.price 告诉 sorted 函数,去拿每个 Product 对象的 price 属性来比
sorted_products_by_price = sorted(products, key=lambda p: p.price)
print(f"按价格排序的商品:{sorted_products_by_price}")
# 输出:按价格排序的商品:[Product(name='Mouse', price=25, stock=200), Product(name='Keyboard', price=75, stock=100), Product(name='Monitor', price=300, stock=30), Product(name='Laptop', price=1200, stock=50)]
登录后复制

通过

lambda
登录后复制
表达式,我们可以非常灵活地定义各种复杂的排序逻辑。它就像一个临时的、小巧的函数,专门为
key
登录后复制
参数服务。

除了
lambda
登录后复制
,还有更高效的排序方式吗?
operator
登录后复制
模块的实践

虽然

lambda
登录后复制
表达式非常方便,但对于某些特定场景,Python 标准库中的
operator
登录后复制
模块提供了更高效、有时也更清晰的替代方案,尤其是当你需要根据字典的键或对象的属性进行排序时。

Poe
Poe

Quora旗下的对话机器人聚合工具

Poe 607
查看详情 Poe

operator.itemgetter
登录后复制
适用于序列(如元组)和映射(如字典),它会返回一个可调用对象,这个对象在被调用时会从其参数中获取指定索引或键的值。

from operator import itemgetter

users = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35},
    {"name": "David", "age": 25}
]

# 用 itemgetter 替代 lambda 来按年龄排序
# itemgetter('age') 会创建一个函数,这个函数接收一个字典,然后返回字典里 'age' 键对应的值
sorted_users_by_age_op = sorted(users, key=itemgetter('age'))
print(f"用 itemgetter 按年龄排序的用户:{sorted_users_by_age_op}")

# itemgetter 还能用于多级排序,传入多个键名或索引,它会返回一个元组
# 先按年龄升序,年龄相同则按名字升序
sorted_users_multi_level = sorted(users, key=itemgetter('age', 'name'))
print(f"多级排序(年龄再姓名):{sorted_users_multi_level}")
# 输出:多级排序(年龄再姓名):[{'name': 'Bob', 'age': 25}, {'name': 'David', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Charlie', 'age': 35}]
# 注意到 Bob 和 David 的顺序变了,因为 David 的 D 在 Bob 的 B 后面
登录后复制

operator.attrgetter
登录后复制
则是为对象属性排序而生。它会返回一个可调用对象,这个对象被调用时会从其参数中获取指定属性的值。

from operator import attrgetter

# 沿用之前的 Product 类和 products 列表
# 按价格排序
sorted_products_by_price_op = sorted(products, key=attrgetter('price'))
print(f"用 attrgetter 按价格排序的商品:{sorted_products_by_price_op}")

# 多级排序:先按库存降序,库存相同再按价格升序
# 注意这里我们不能直接在 attrgetter 里指定降序,降序要靠外层的 reverse=True
# 但如果某个键需要降序,另一个需要升序,就需要更复杂的 key 函数或者分步排序了
# 这里先按库存,再按价格
sorted_products_multi_level = sorted(products, key=attrgetter('stock', 'price'), reverse=True) # 整体降序
print(f"多级排序(库存降序,再价格降序):{sorted_products_multi_level}")
# 如果想要库存降序,价格升序,就需要稍微调整 key 函数的返回:
# sorted_products_mixed_order = sorted(products, key=lambda p: (-p.stock, p.price))
# 通过对降序的数值取负,可以实现混合排序
登录后复制

使用

itemgetter
登录后复制
attrgetter
登录后复制
的好处在于,它们通常比等效的
lambda
登录后复制
表达式在性能上略有优势,尤其是在处理大量数据时,因为它们在 C 语言层面实现了优化。而且,它们的意图也更明确,代码可读性有时会更好。

Python 排序是“稳定”的吗?这对你的数据处理意味着什么?

这是一个很重要的特性:Python 的

sorted()
登录后复制
函数,以及列表的
sort()
登录后复制
方法,都是稳定排序算法。这意味着什么呢?简单来说,如果你的数据中有两个或多个元素,它们在排序键上的值是相同的,那么它们在排序后的相对顺序,会和它们在原始列表中的相对顺序保持一致。

举个例子:

data_with_duplicates = [("apple", 5), ("banana", 2), ("cherry", 8), ("date", 1), ("grape", 5)]

# 按第二个元素排序
# 'apple' 和 'grape' 的第二个元素都是 5
sorted_stable = sorted(data_with_duplicates, key=lambda x: x[1])
print(f"稳定排序示例:{sorted_stable}")
# 输出:稳定排序示例:[('date', 1), ('banana', 2), ('apple', 5), ('grape', 5), ('cherry', 8)]
# 注意,('apple', 5) 在 ('grape', 5) 之前,排序后它们依然保持了这个相对顺序。
登录后复制

这种稳定性在多级排序或者当你关心原始顺序时非常有用。比如,你先按年龄排序,然后又想对年龄相同的人按名字排序,如果排序不稳定,你之前年龄排序的“局部顺序”可能就被打乱了。

至于性能考量,

sorted()
登录后复制
函数通常采用 Timsort 算法,这是一种混合了归并排序和插入排序的算法。它的平均和最坏时间复杂度都是 O(N log N),其中 N 是元素的数量。对于大多数实际应用来说,这个性能是相当不错的。

需要注意的一点是,

key
登录后复制
函数的计算成本。如果你的
key
登录后复制
函数本身执行了非常复杂的计算,那么每次比较元素时都会调用它,这会显著增加排序的总时间。所以,在设计
key
登录后复制
函数时,尽量保持其简洁和高效。

最后,我们简单提一下

sorted()
登录后复制
list.sort()
登录后复制
的选择。

  • sorted(iterable)
    登录后复制
    :返回一个新的已排序列表,不改变原始数据。当你需要保留原始列表的同时获取一个排序版本时,用它。
  • list.sort()
    登录后复制
    :是列表对象的一个方法,它会原地修改列表,不返回任何值(返回
    None
    登录后复制
    )。如果你不需要保留原始列表,并且想节省一点内存(因为它不需要创建新列表),那么
    list.sort()
    登录后复制
    可能是更好的选择。它们都支持
    key
    登录后复制
    reverse
    登录后复制
    参数,用法基本一致。选择哪个,就看你对原始数据有没有“洁癖”了。

以上就是Python怎样实现数据排序?sorted函数进阶的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号