话说回来,咱们搞开发,数据处理是家常便饭,而排序,那简直就是家常便饭里的榨菜丝儿,虽小,没它还真不行。特别是 Python 这玩意儿,说它语法优雅得跟诗似的,那是一点不夸张,可真要说起“排序”这档子事儿,嘿,门道还真不少,不是光知道个 sorted() 或者 .sort() 就万事大吉的。我呢,也趟过不少坑,才慢慢摸索出点儿眉目,今儿就来跟你唠唠,Python 怎么排序,那些你可能没留意的细节和高级玩法。

先别急,咱们从最基础的开始。Python 里排序,最常用的两个“家伙”就是 list.sort() 方法和内置的 sorted() 函数。这俩长得像,干的活儿也差不多,但骨子里可是天壤之别。

list.sort() 来说,这是列表(list)特有的一个方法。啥叫方法?就是这动作得列表自己来完成。你让一个列表 my_list 调用 my_list.sort(),它就吭哧吭哧地把自己内部的元素给原地排序了。啥叫原地?就是它自己变了,没生成个新的列表给你。这就像你整理自己的书架,书还是那些书,位置变了,但书架本身没变。所以,调用完 my_list.sort() 后,原来的 my_list 就已经是排好序的样子了。这种方式呢,效率挺高,因为它不需要额外的内存空间去存一个新的排序结果,直接在原地址上操作。但是,有个明显的副作用就是:原列表变了!如果你还想保留原列表未排序的样子,那就得先复制一份,再对复制件进行排序,有点麻烦。而且,记住,list.sort() 只适用于列表!你想给元组、字符串啥的排序?没门!它会返回 None,表示操作完成了,而不是返回排序后的列表。这 None 可别小瞧,多少新手栽在这儿,以为 sorted_list = my_list.sort() 就能得到排序后的列表,结果 sorted_list 永远是 None,然后一脸懵逼。

再说 sorted() 函数,这可是个内置函数,地位高多了,谁都能用!不挑食。给它一个可迭代对象(iterable),比如列表、元组、字符串、字典的键、集合等等,它就会老老实实地给你返回一个新的、排好序的列表。注意了,是列表!哪怕你给它的是个元组或者集合,它吐出来的也是列表。这就像你把一堆杂七杂八的东西扔进一个“ sorting machine ”(排序机),机器咔咔一顿操作,然后给你吐出来一摞整整齐齐的东西,但这摞东西是全新的,你原来那堆东西还在那儿,没动。所以,sorted()好处在于它不会改变原始数据,很安全。你想保留原始顺序又需要一个排序后的版本?sorted() 就是你的菜。缺点嘛,相对 list.sort() 来说,它需要额外的内存空间来创建一个新的列表,对于超大规模数据,可能会稍微耗费些资源。

好了,基本工具箱里这俩宝贝儿算是认识了。光认识工具不行啊,还得知道怎么用活了,特别是怎么按自己的规矩来排序。默认情况下,无论是 sort() 还是 sorted(),都是按照元素的自然顺序来的。数字按大小,字符串按字典序。但如果你的数据是更复杂的呢?比如一个学生名单,每个学生是个字典,包含姓名、年龄、分数,你想按分数从高到低排,或者先按班级再按年龄排?这时候,就得请出排序的“灵魂伴侣”:key 参数

key 参数是个函数,这个函数接收一个元素作为输入,然后返回一个值,排序时就根据这个函数返回的值来比较。想象一下,你有一堆学生的“档案袋”(就是那些字典),你不看档案袋封面(原始数据),而是从每个档案袋里掏出一张“分数条”,然后你就只看这张“分数条”上的数字来排列这些档案袋。这个“掏出分数条”的动作,就是 key 函数干的事儿。

举个栗子。假设我们有个列表,里面是字典:
python
students = [
{'name': '小明', 'age': 18, 'score': 95},
{'name': '小红', 'age': 19, 'score': 88},
{'name': '小刚', 'age': 18, 'score': 92},
]

我想按分数从低到高排。默认排不了,因为直接比较字典没意义。我就得告诉排序函数,“看分数!”。这时候 key 就上场了。我们可以用一个 lambda 匿名函数来指定 key
“`python
sorted_by_score = sorted(students, key=lambda student: student[‘score’])

lambda student: student[‘score’] 这个函数,对于列表里的每一个student(也就是每个字典),它都会返回 student[‘score’] 的值。

sorted() 函数就拿这些分数来做比较,进行排序。

print(sorted_by_score)
“`
输出就会是按分数升序排列的结果。

那如果想按分数从高到低呢?又请出排序的另一个“调味剂”:reverse 参数

reverse 参数是个布尔值TrueFalse。默认是 False,表示升序(从小到大,字典序从A到Z)。如果你设成 True,那就是降序(从大到小,字典序从Z到A)。

接着上面的栗子,想按分数从高到低排:
python
sorted_by_score_desc = sorted(students, key=lambda student: student['score'], reverse=True)
print(sorted_by_score_desc)

这下,分数最高的学生就在最前面了。

keyreverse 这俩参数,就像排序的“左右手”,配合使用,几乎能满足你所有的定制排序需求

除了 lambda 函数,key 也可以是任何接收一个参数并返回一个值的函数。比如,你想忽略字符串大小写进行排序,可以用 str.lower 作为 key
python
words = ['Banana', 'apple', 'Orange', 'grape']
sorted_words = sorted(words, key=str.lower)
print(sorted_words) # 输出 ['apple', 'Banana', 'grape', 'Orange'],注意大小写被忽略了

再比如,你想根据一个对象的某个属性排序,可以直接把这个属性拿来当 key 的返回值。如果对象的属性比较复杂,或者需要多层访问,你可以写个更复杂的函数,甚至引入 operator 模块里的 itemgetterattrgetter,写起来更简洁,效率也更高一些。比如用 itemgetter 按分数排序:
python
from operator import itemgetter
sorted_by_score_op = sorted(students, key=itemgetter('score'))
print(sorted_by_score_op) # 效果同 lambda student: student['score']

itemgetter('score') 就像一个小型工厂,你扔进去一个字典,它就帮你把 key 为 'score' 的值“生产”出来。用 attrgetter 则是针对对象的属性。这俩是写 Python 代码时显得更“专业”的小技巧,但核心思想还是 key 函数的作用。

多重排序呢?比如先按年龄升序,年龄相同的再按分数降序?没问题!key 函数返回的值,可以是元组!Python 在比较元组时,会逐个元素比较。先比较元组的第一个元素,第一个元素相同时,再比较第二个元素,以此类推。

所以,要实现先按年龄升序,再按分数降序,我们的 key 函数就得返回一个元组,元组的第一个元素是年龄(用于升序),第二个元素是分数(用于降序)。等等,如果第二个元素直接是分数,那默认是升序啊?怎么变降序?这里有个小技巧:想让某个数字类型的字段按降序排,可以在其前面加个负号!

“`python
sorted_by_age_score = sorted(students, key=lambda student: (student[‘age’], -student[‘score’]))

key 函数返回 (年龄, -分数)。

Python 先比年龄,年龄小的在前。年龄一样?好,接着比第二个元素:-分数。

-95 比 -88 小,所以分数 95 的会排在分数 88 的前面。这就实现了分数降序。

print(sorted_by_age_score)
“`
看到没?利用元组和负号的小魔法,多重排序也能玩转!这个技巧在处理复杂排序需求时特别实用。

除了这些,还有一些稍微不那么常用,但在特定场景下很给力的排序方式,比如 bisect 模块,它不是用来完全排序一个列表的,而是用来在一个已经排好序的列表快速查找插入位置,以保持排序状态。这在需要频繁插入数据到有序列表时特别有用,效率比每次插入后再完全排序高多了。但它要求你的列表必须先是有序的。

再提一嘴,Python 3 的排序算法用的是Timsort,这是一种混合排序算法,结合了归并排序插入排序的优点。它在处理部分有序的数据时效率尤其高,这也是为什么 Python 的排序通常感觉“挺快”的原因之一。了解这个呢,不是说你写代码时要直接用 Timsort,而是知道 Python 的内部机制很智能,对于大多数排序任务,内置的 sorted().sort() 已经足够强大和高效了。

总结一下,Python 怎么排序?掌握 list.sort()sorted() 的区别和使用场景,理解 key 参数的作用和如何使用 lambdaitemgetter 等来定制排序规则,学会利用 reverse 参数控制升降序,以及通过 key 返回元组实现多重排序。这些就是 Python 排序的“十八般武艺”。实战中多练练,遇到各种奇奇怪怪的排序需求,都能做到心里有底,不再抓瞎。别光看,找点数据自己动手试试!练起来才知道这些东西是不是真的“进脑子”了。

希望这篇唠叨,能帮你把 Python 排序这块儿的“迷雾”拨开点,让你以后写代码碰到排序,能自信地说:“小样儿,就这点事儿!”

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。