尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python列表排序详解:从基础到高级技巧

Python列表排序详解:从基础到高级技巧 1. Python列表排序基础原理Python提供了两种主要的列表排序方式内置的list.sort()方法和sorted()函数。这两者的核心区别在于list.sort()是列表对象的方法会直接修改原列表原地排序返回值为Nonesorted()是内置函数接受任何可迭代对象作为输入返回一个新的已排序列表# 使用sorted()函数 numbers [5, 2, 3, 1, 4] sorted_numbers sorted(numbers) print(sorted_numbers) # 输出[1, 2, 3, 4, 5] print(numbers) # 原列表不变[5, 2, 3, 1, 4] # 使用list.sort()方法 numbers.sort() print(numbers) # 原列表被修改[1, 2, 3, 4, 5]注意对于大型数据集list.sort()通常比sorted()效率稍高因为它不需要创建新列表。但如果你需要保留原始数据就必须使用sorted()。2. 关键参数key的深度解析key参数是Python排序功能中最强大的特性之一它允许我们指定一个函数或任何可调用对象这个函数会作用在每个元素上返回一个用于排序的键。2.1 基本key函数使用words [banana, pie, Washington, book] # 按字符串长度排序 print(sorted(words, keylen)) # 输出[pie, book, banana, Washington] # 不区分大小写排序 print(sorted(This is a test string from Andrew.split(), keystr.casefold)) # 输出[a, Andrew, from, is, string, test, This]2.2 复杂对象的排序当处理包含复杂结构的数据时key函数特别有用student_tuples [ (john, A, 15), (jane, B, 12), (dave, B, 10), ] # 按年龄排序元组的第三个元素 print(sorted(student_tuples, keylambda student: student[2])) # 输出[(dave, B, 10), (jane, B, 12), (john, A, 15)]对于自定义类对象同样适用class Student: def __init__(self, name, grade, age): self.name name self.grade grade self.age age def __repr__(self): return repr((self.name, self.grade, self.age)) student_objects [ Student(john, A, 15), Student(jane, B, 12), Student(dave, B, 10), ] # 按年龄属性排序 print(sorted(student_objects, keylambda student: student.age)) # 输出[(dave, B, 10), (jane, B, 12), (john, A, 15)]2.3 operator模块的辅助函数Python的operator模块提供了几个有用的函数来简化key函数的编写from operator import itemgetter, attrgetter # 使用itemgetter处理元组 print(sorted(student_tuples, keyitemgetter(2))) # 输出[(dave, B, 10), (jane, B, 12), (john, A, 15)] # 使用attrgetter处理对象属性 print(sorted(student_objects, keyattrgetter(age))) # 输出[(dave, B, 10), (jane, B, 12), (john, A, 15)]operator函数还支持多级排序# 先按grade排序再按age排序 print(sorted(student_tuples, keyitemgetter(1,2))) # 输出[(john, A, 15), (dave, B, 10), (jane, B, 12)]3. 反向排序与稳定性3.1 reverse参数通过设置reverseTrue可以实现降序排序numbers [5, 2, 3, 1, 4] print(sorted(numbers, reverseTrue)) # 输出[5, 4, 3, 2, 1]3.2 排序稳定性Python的排序是稳定的这意味着当多个元素具有相同的键时它们的相对顺序会保持不变data [(red, 1), (blue, 1), (red, 2), (blue, 2)] # 按颜色排序后相同颜色的元组保持原有顺序 print(sorted(data, keyitemgetter(0))) # 输出[(blue, 1), (blue, 2), (red, 1), (red, 2)]3.3 实现多级排序利用排序稳定性我们可以通过多次排序实现复杂的排序需求# 先按age升序排序 s sorted(student_objects, keyattrgetter(age)) # 再按grade降序排序 print(sorted(s, keyattrgetter(grade), reverseTrue)) # 输出[(dave, B, 10), (jane, B, 12), (john, A, 15)]或者封装成一个函数def multisort(xs, specs): for key, reverse in reversed(specs): xs.sort(keyattrgetter(key), reversereverse) return xs print(multisort(list(student_objects), ((grade, True), (age, False)))) # 输出[(dave, B, 10), (jane, B, 12), (john, A, 15)]4. 高级排序技巧与性能优化4.1 装饰-排序-去装饰模式这是一种经典的排序技术特别适用于计算键值开销较大的情况# 原始数据 students [dave, john, jane] grades {john: F, jane:A, dave: C} # 装饰阶段创建(grade, index, student)元组 decorated [(grades[student], i, student) for i, student in enumerate(students)] # 排序 decorated.sort() # 去装饰提取原始数据 result [student for grade, i, student in decorated] print(result) # 输出[jane, dave, john]4.2 使用functools.partial创建key函数当需要固定某些参数时partial函数非常有用from functools import partial from unicodedata import normalize names Zoë Åbjørn Núñez Élana Zeke Abe Nubia Eloise.split() # 使用partial固定normalize函数的第一个参数 print(sorted(names, keypartial(normalize, NFD))) # 输出[Abe, Åbjørn, Eloise, Élana, Nubia, Núñez, Zeke, Zoë]4.3 自定义比较函数虽然不推荐因为性能较差但Python仍然支持传统的比较函数from functools import cmp_to_key def compare(a, b): if a.age b.age: return -1 elif a.age b.age: return 1 else: return 0 print(sorted(student_objects, keycmp_to_key(compare))) # 输出[(dave, B, 10), (jane, B, 12), (john, A, 15)]5. 实际应用中的性能考量5.1 选择正确的排序方法对于小型列表1000元素任何方法差异不大对于中型列表1000-10000元素list.sort()通常比sorted()快10-20%对于大型列表10000元素考虑使用更专业的排序算法5.2 key函数的性能影响key函数会被频繁调用因此其性能直接影响整体排序速度# 低效的key函数 sorted(data, keylambda x: expensive_calculation(x)) # 更高效的做法如果可能 precomputed [(expensive_calculation(x), x) for x in data] precomputed.sort() result [x for (key, x) in precomputed]5.3 使用bisect模块维护有序列表对于需要频繁插入并保持有序的场景import bisect sorted_list [] for item in data: bisect.insort(sorted_list, item) # 保持列表有序的同时插入新元素6. 常见问题与解决方案6.1 混合类型排序问题Python 3不再支持不同类别的隐式比较# Python 3中会抛出TypeError try: sorted([1, 2, 3]) except TypeError as e: print(f错误{e})解决方案是确保key函数返回可比较的类型print(sorted([1, 2, 3], keystr)) # 转换为字符串比较6.2 处理None值data [1, None, 3, 2, None] # 方法1过滤None值 print(sorted((x for x in data if x is not None))) # 方法2指定处理None的key函数 print(sorted(data, keylambda x: float(inf) if x is None else x))6.3 多条件排序的优先级当需要按多个条件排序时条件的顺序很重要# 先按grade降序再按age升序 print(sorted(student_objects, keylambda x: (-ord(x.grade), x.age))) # 输出[(john, A, 15), (dave, B, 10), (jane, B, 12)]6.4 大型数据集的排序对于内存无法容纳的超大型数据集使用数据库的ORDER BY实现外部排序算法考虑使用heapq模块的部分排序功能import heapq # 获取最大的3个元素 print(heapq.nlargest(3, student_objects, keyattrgetter(age))) # 输出[(john, A, 15), (jane, B, 12), (dave, B, 10)]7. 实际案例电商产品排序假设我们有一个电商产品列表需要实现多种排序方式products [ {name: Laptop, price: 999.99, rating: 4.5, sales: 1200}, {name: Phone, price: 699.99, rating: 4.2, sales: 3500}, {name: Tablet, price: 299.99, rating: 3.9, sales: 2500}, {name: Headphones, price: 149.99, rating: 4.7, sales: 8000}, ] # 按价格升序 print(sorted(products, keylambda x: x[price])) # 按评分降序销量降序 print(sorted(products, keylambda x: (-x[rating], -x[sales]))) # 按名称长度然后按价格 print(sorted(products, keylambda x: (len(x[name]), x[price])))8. 排序算法内部机制Python使用的Timsort算法结合了归并排序和插入排序的优点查找数据中已经有序的run使用插入排序扩展这些run到最小长度使用归并排序合并这些run这种算法特别适合部分有序的数据包含多个有序段的数据各种规模的数据集理解这一点有助于我们编写更高效的key函数因为Timsort会尝试利用数据中已有的顺序。
返回列表