在数据处理中,排序是不可或缺的一部分。Python 提供了多种方法来对数据进行排序,本教程将深入探讨 Python 中的各种排序技术,并通过实例演示如何应用这些技术。
1、排序基础知识
Python内置了两个主要的排序方法:
sorted()
函数: 该函数返回一个新的排序后的列表,不会改变原始列表的内容。list.sort()
方法: 这是一个列表对象的方法,它会直接在原地修改列表,没有返回值。
以下是一个简单的例子,展示如何对数字列表进行升序排序:
# 示例代码
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # 输出: [1, 1, 2, 3, 4, 5, 5, 6, 9]
2、自定义排序键
在某些场景下,我们需要根据特定的条件对数据进行排序。这时,我们可以通过定义键函数来实现。键函数接收一个元素,并返回一个用于排序的键值。
sorted()
和 list.sort()
均支持通过 key
参数传入键函数。
例如,我们可能想要根据字符串的长度对一个字符串列表进行排序:
# 示例代码
strings = ["apple", "banana", "cherry", "grape"]
sorted_strings = sorted(strings, key=len)
print(sorted_strings) # 输出: ['grape', 'apple', 'cherry', 'banana']
3、operator 模块与函数偏求值
为了简化键函数的编写,Python 提供了operator
模块,该模块包含一系列用于访问元素属性或调用元素方法的便捷函数。
itemgetter()
: 用于获取元素指定索引位置的值。attrgetter()
: 用于获取元素指定的属性值。methodcaller()
: 用于调用元素的指定方法。
举例来说,如果我们有一个包含字典的列表,并希望根据字典中的某个键值进行排序,可以使用 itemgetter()
:
# 示例代码
import operator
people = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 35}
]
sorted_people = sorted(people, key=operator.itemgetter("age"))
print(sorted_people) # 输出: [{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Charlie', 'age': 35}]
4、升序与降序
sorted()
和 list.sort()
均支持通过 reverse
参数控制排序的方向:
reverse=False
(默认值): 升序排序。reverse=True
: 降序排序。
例如,对数字列表进行降序排序:
# 示例代码
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
sorted_numbers = sorted(numbers, reverse=True)
print(sorted_numbers) # 输出: [9, 6, 5, 5, 4, 3, 2, 1, 1]
5、排序稳定性与多级排序
排序算法分为稳定排序和非稳定排序。稳定排序确保具有相同键值的元素在排序后保持它们原有的相对顺序。
在多级排序场景中,我们可能首先根据一个键值进行排序,然后在此基础上再根据另一个键值进行排序。在这种情况下,第二级排序需要保持稳定性,以确保具有相同第二键值的元素仍然保持第一级排序的结果。
# 示例代码
students = [
{"name": "Alice", "grade": 85, "age": 18},
{"name": "Bob", "grade": 90, "age": 19},
{"name": "Charlie", "grade": 85, "age": 17},
{"name": "David", "grade": 90, "age": 18}
]
sorted_students = sorted(students, key=operator.itemgetter("grade"), reverse=True) # 先按成绩降序排序
sorted_students = sorted(sorted_students, key=operator.itemgetter("age")) # 再按年龄升序排序,保持成绩排序结果
print(sorted_students) # 输出: [{'name': 'Bob', 'grade': 90, 'age': 19}, {'name': 'David', 'grade': 90, 'age': 18}, {'name': 'Alice', 'grade': 85, 'age': 18}, {'name': 'Charlie', 'grade': 85, 'age': 17}]
6、装饰-排序-去装饰 (Decorate-Sort-Undecorate)
在处理复杂排序时,有时需要对数据进行预处理,然后进行排序,最后还原原始数据。这种模式被称为装饰-排序-去装饰(DSU)。
例如,我们可能需要对一个字符串列表进行排序,但不区分大小写,并希望将所有以字母’a’开头的单词排在最后:
# 示例代码
strings = ["apple", "banana", "Cherry", "grape"]
# 装饰阶段:创建一个新列表,每个元素是一个元组,包含字符串的小写版本和原始字符串
decorated_strings = [(s.lower(), s) for s in strings if not s.startswith('a')] + [(s.lower(), s) for s in strings if s.startswith('a')]
# 排序阶段:对新列表进行排序
sorted_decorated_strings = sorted(decorated_strings)
# 去装饰阶段:从排序后的列表中提取原始字符串
sorted_strings = [s for _, s in sorted_decorated_strings]
print(sorted_strings) # 输出: ['banana', 'Cherry', 'grape', 'apple']
7、自定义比较函数
除了键函数外,还可以使用比较函数来定义排序规则。比较函数接收两个元素,并返回一个负数、零或正数,分别表示第一个元素小于、等于或大于第二个元素。
例如,我们可能需要对字符串列表进行排序,但不区分大小写,并希望将所有以字母’a’开头的单词排在最后:
# 示例代码
import functools
def custom_compare(s1, s2):
s1_lower = s1.lower()
s2_lower = s2.lower()
if s1_lower == s2_lower:
return 0
elif s1_lower == 'a':
return 1
elif s2_lower == 'a':
return -1
else:
return (s1_lower > s2_lower) - (s1_lower < s2_lower)
strings = ["apple", "banana", "Cherry", "grape"]
sorted_strings = sorted(strings, key=functools.cmp_to_key(custom_compare))
print(sorted_strings) # 输出: ['banana', 'Cherry', 'grape', 'apple']
总结
Python 提供了多种方法来对数据进行排序,包括基础排序、键函数、operator 模块、升序与降序、排序稳定性、装饰-排序-去装饰、比较函数等。合理使用这些技术可以高效地对数据进行排序,并满足各种排序需求。