云南网站建设,企业信息化软件定制开发

专业提供昆明网站建设, 昆明软件开发, 云南网站建设,企业信息化软件定制开发服务免费咨询QQ932256355

博客园 首页 新随笔 联系 订阅 管理

Python 排序秘籍:从基础到进阶的全面指南

本文是 Python 排序技术的深度教程,旨在帮助读者全面掌握 Python 中的排序操作。从基础的列表排序方法,到使用键函数、运算符模块函数实现复杂对象排序,再到装饰 - 排序 - 去装饰技术、比较函数的应用以及部分排序的技巧等,都进行了详细讲解。结合丰富的实际项目中的排序示例,通过示例代码、图文以及对比表格,助力读者轻松理解并熟练运用排序技术,提升编程效率。

排序的基础知识

Python 提供了两种主要的排序方式:内置函数sorted()和列表方法list.sort()sorted()函数接受任何可迭代对象,返回一个新的有序列表;list.sort()方法仅适用于列表,它会原地修改列表并返回None 。如果需要保留原始数据,sorted()更为合适;若无需保留原始列表,list.sort()在性能上会略胜一筹。

排序方式 适用对象 操作特点 返回值 示例
sorted() 可迭代对象 创建新的有序列表 有序列表 sorted([5, 2, 3, 1, 4]) -> [1, 2, 3, 4, 5] sorted({1:'D', 2:'B', 3:'B', 4:'E', 5:'A'}) -> [1, 2, 3, 4, 5]
list.sort() 列表 原地修改列表 None a = [5, 2, 3, 1, 4]; a.sort(); a -> [1, 2, 3, 4, 5]

键函数

list.sort()sorted()都支持key参数,用于指定在比较前对每个列表元素调用的函数。这在处理复杂对象或需要特定排序规则时非常有用。

  • 字符串不区分大小写排序:在文本处理中,对包含大小写混合的单词列表进行排序时,不希望区分大小写。例如在一个图书管理系统中,图书标签列表['Python', 'java', 'C++', 'Ruby'],使用sorted(["Python", "java", "C++", "Ruby"], key=str.casefold),可以得到按不区分大小写排序的结果['C++', 'java', 'Python', 'Ruby']
  • 按对象属性排序:在学生成绩管理系统中,学生信息用字典表示,包含name(姓名)、score(成绩)等键值对。假设有学生列表students = [{'name': 'Alice','score': 85}, {'name': 'Bob','score': 70}, {'name': 'Charlie','score': 90}],使用sorted(students, key=lambda student: student['score'])可以按成绩对学生进行升序排序。

运算符模块的函数与函数的偏求值

为简化键函数的编写,Python 的operator模块提供了itemgetter()attrgetter()methodcaller()等函数。

  • 使用itemgetter()对元组列表排序:在一个存储城市坐标的项目中,城市坐标用元组(latitude, longitude)表示,如cities = [(37.7749, -122.4194), (40.7128, -74.0060), (34.0522, -118.2437)],使用from operator import itemgetter; sorted(cities, key=itemgetter(0))可以按纬度对城市进行排序。
  • 使用attrgetter()对对象列表排序:在员工管理系统中,员工用类表示,有name(姓名)、age(年龄)等属性。假设有员工类class Employee: def __init__(self, name, age): self.name = name; self.age = age,以及员工列表employees = [Employee('Alice', 25), Employee('Bob', 30), Employee('Charlie', 22)],使用from operator import attrgetter; sorted(employees, key=attrgetter('age'))可以按年龄对员工进行排序。
  • 使用partial()函数创建键函数:在处理 Unicode 字符串时,需要将字符串标准化后再排序。例如有名字列表names = ['Zoë', 'Åbjørn', 'Núñez', 'Élana'],使用from functools import partial; from unicodedata import normalize; sorted(names, key=partial(normalize, 'NFD'))可以按特定的 Unicode 标准化形式进行排序。

升序与降序

list.sort()sorted()都接受reverse布尔参数,用于控制排序顺序。reverse=True表示降序排序,reverse=False(默认值)表示升序排序。

  • 成绩排名系统:在考试成绩排名中,假设有成绩列表scores = [85, 90, 75, 80],使用sorted(scores, reverse=True)可以得到降序排列的成绩列表[90, 85, 80, 75],方便查看成绩从高到低的排名。
  • 商品销量排名:在电商数据分析中,商品销量数据用列表表示,如sales = [100, 200, 50, 150],使用sorted(sales, reverse=True)可以按销量从高到低对商品进行排名。

排序稳定性与复杂排序

Python 的排序算法是稳定的,即相等键的记录会保持原始顺序。这一特性在构建复杂排序时非常关键。

  • 学生成绩综合排序:在学生成绩管理中,要先按成绩降序,成绩相同再按年龄升序排序。假设有学生对象列表students = [Student('Alice', 85, 20), Student('Bob', 85, 22), Student('Charlie', 90, 19)],可以先按年龄升序排序sorted_students_by_age = sorted(students, key=attrgetter('age')),再按成绩降序排序sorted_students = sorted(sorted_students_by_age, key=attrgetter('score'), reverse=True),从而实现综合排序。
  • 文件按修改时间和大小排序:在文件管理系统中,文件信息用类表示,有name(文件名)、mod_time(修改时间)、size(文件大小)等属性。假设有文件类class File: def __init__(self, name, mod_time, size): self.name = name; self.mod_time = mod_time; self.size = size,以及文件列表files = [File('file1', '2024-01-01', 1024), File('file2', '2024-01-02', 1024), File('file3', '2024-01-01', 2048)],先按文件大小升序排序sorted_files_by_size = sorted(files, key=attrgetter('size')),再按修改时间降序排序sorted_files = sorted(sorted_files_by_size, key=attrgetter('mod_time'), reverse=True),实现先按大小、再按修改时间的复杂排序。

装饰 - 排序 - 去装饰

装饰 - 排序 - 去装饰(Decorate-Sort-Undecorate,简称 DSU),也叫 Schwartzian transform,是一种排序技术。它包括三个步骤:首先用控制排序顺序的新值装饰初始列表;然后排序装饰后的列表;最后去除装饰得到按新顺序排列的初始值列表 。

  • 歌曲列表排序:在音乐播放列表管理中,歌曲信息用字典表示,包含title(歌名)、duration(时长)等键值对。假设有歌曲列表songs = [{'title':'song1', 'duration': 180}, {'title':'song2', 'duration': 240}, {'title':'song3', 'duration': 180}],使用 DSU 方法可以这样排序:
decorated = [(song['duration'], i, song) for i, song in enumerate(songs)]
decorated.sort()
sorted_songs = [song for _, _, song in decorated]
print(sorted_songs)

在这个示例中,通过装饰步骤为每个歌曲添加了时长和索引信息,排序后再去除装饰,得到按时长升序排列的歌曲列表。

比较函数

比较函数用于计算两个输入的相对排序,返回负值表示小于,零表示相等,正值表示大于。当从其他语言转写算法或使用某些提供比较函数的库(如locale.strcoll())时,会用到比较函数。Python 的functools.cmp_to_key()函数可以将比较函数包装成键函数,以便在sorted()list.sort()中使用 。

  • 自定义比较函数实现特定排序:在一个竞赛排名系统中,选手成绩用类表示,有score(分数)和penalty(罚时)两个关键属性。排名规则是分数高的排名靠前,分数相同则罚时少的排名靠前。可以定义比较函数def compare_players(player1, player2): if player1.score != player2.score: return player2.score - player1.score; return player1.penalty - player2.penalty,然后使用from functools import cmp_to_key; players = [Player(85, 10), Player(90, 5), Player(85, 5)]; sorted_players = sorted(players, key=cmp_to_key(compare_players))对选手进行排序。

杂项说明

  1. 语言区域感知排序:在一个国际化的文本处理项目中,处理包含不同语言的文本数据。假设要对法语单词列表['été', 'automne', 'printemps', 'hiver']按照法语的字母顺序排序,使用import locale; locale.setlocale(locale.LC_ALL, 'fr_FR.UTF-8'); sorted_words = sorted(['été', 'automne', 'printemps', 'hiver'], key=locale.strxfrm)可以得到正确的排序结果。
  2. 反向排序的替代方法:在对一个包含颜色和编号的元组列表data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]进行反向排序时,可以使用reverse参数sorted(data, key=lambda x: x[0], reverse=True),也可以使用两次reversed()函数double_reversed = list(reversed(sorted(reversed(data), key=lambda x: x[0]))),两种方法得到的结果相同。
  3. 为类添加排序方法:在自定义的几何图形类中,如class Rectangle: def __init__(self, width, height): self.width = width; self.height = height,为了能按面积对矩形进行排序,可以定义__lt__()方法def __lt__(self, other): return self.width * self.height < other.width * other.height,然后使用sorted([Rectangle(3, 4), Rectangle(2, 5), Rectangle(4, 4)])即可按面积升序排序。
  4. 键函数与外部资源:在一个学生选课系统中,学生的课程成绩存储在字典中,如grades = {'Alice': 85, 'Bob': 70, 'Charlie': 90},而学生名单在列表中students = ['Bob', 'Alice', 'Charlie']。要根据成绩对学生名单排序,可以使用sorted(students, key=grades.__getitem__)

部分排序

在某些场景下,只需要对部分数据进行排序。Python 标准库提供了以下工具:

  1. 查找最值:在一个体育比赛成绩统计项目中,有运动员的成绩列表scores = [85, 90, 75, 80, 95],使用min(scores)可以快速找到最低分,max(scores)可以找到最高分。
  2. 获取部分最值:在股票交易数据分析中,有股票价格列表prices = [100, 120, 90, 130, 110, 140],如果只想获取价格最高的 3 只股票,使用import heapq; top_3_prices = heapq.nlargest(3, prices)可以实现。
  3. 优先级队列:在任务调度系统中,使用heapq.heappush()heapq.heappop()实现优先级队列。假设有任务类class Task: def __init__(self, priority, task_name): self.priority = priority; self.task_name = task_name,可以将任务按优先级加入队列task_queue = []; heapq.heappush(task_queue, Task(2, 'task1')); heapq.heappush(task_queue, Task(1, 'task2')),然后使用heapq.heappop(task_queue)按优先级取出任务。

sorted()函数和list.sort()方法的时间复杂度

在 Python 中,sorted()函数和list.sort()方法底层都使用了 Timsort 算法,平均时间复杂度和最坏时间复杂度都是 O(nlogn) ,其中 n 是待排序元素的数量。

Timsort 算法是一种自适应算法,它会根据输入数据的特点选择不同的排序策略。如果输入数据已经部分有序,Timsort 会利用这一特性,采用归并排序的思想,将有序的子序列合并起来,从而提高排序效率。在最好情况下,即输入数据完全有序时,Timsort 的时间复杂度可以达到 ,因为它只需要对数据进行一次线性扫描,确认其有序性即可。

虽然list.sort()sorted()的时间复杂度相同,但它们在性能上还是有细微差异的。list.sort()是列表对象的方法,它直接在原列表上进行排序,不会创建新的列表对象,因此在空间复杂度上更优,为 。而sorted()函数会返回一个新的有序列表,原输入可迭代对象保持不变,这意味着它的空间复杂度是 ,因为需要额外的空间来存储新的列表。

以下是一个简单的示例,展示了两者在时间复杂度上的一致性(这里主要通过比较大列表的排序时间来体现,实际测试可能会受多种因素影响):

import time

# 生成一个大列表
data = list(range(100000, 0, -1))

# 使用sorted()函数
start_time = time.time()
sorted_data = sorted(data)
sorted_time = time.time() - start_time

# 使用list.sort()方法
data_copy = data.copy()
start_time = time.time()
data_copy.sort()
list_sort_time = time.time() - start_time

print(f"sorted()函数运行时间: {sorted_time} 秒")
print(f"list.sort()方法运行时间: {list_sort_time} 秒")

在上述示例中,对一个较大的逆序列表分别使用sorted()函数和list.sort()方法进行排序,并记录各自的运行时间。可以发现,在处理大规模数据时,两者的运行时间在量级上是相近的,这也间接反映了它们相似的时间复杂度特性。

总结

本文详细介绍了 Python 中的排序技术,涵盖了从基础排序方法到复杂排序场景的多种应用,包括键函数的使用、运算符模块的辅助、排序稳定性的利用、装饰 - 排序 - 去装饰技术、比较函数的适配以及部分排序的实现等内容。通过大量实际项目中的排序示例,进一步展示了这些技术在真实场景中的应用方式。掌握这些知识,读者能够根据不同的需求,灵活选择合适的排序方式,高效地处理数据排序任务。

TAG: Python;排序;sorted 函数;list.sort 方法;键函数;排序稳定性;装饰 - 排序 - 去装饰;比较函数;部分排序;电商排序;日志排序;任务调度排序

相关学习资源

  1. Python 官方文档排序相关文档,本文主要参考资料,提供了丰富的官方示例和详细说明。
  2. Python 教程网站菜鸟教程 - Python 排序,有更多基础到进阶的排序示例,适合初学者巩固知识。
posted on 2025-02-20 15:32  TekinTian  阅读(15)  评论(0)    收藏  举报