在某种程度上,所有的程序员都必须编写代码对项目或数据进行排序。在应用程序当中,排序对于用户体验而言是非常重要的,无论是按时间戳对用户最近的活动进行排序,还是按照姓氏的字母顺序排列电子邮件收件人列表。Python的排序功能十分强大,可以在粒度级别上进行基本排序或自定义排序。
在本教程中,你将会学习如何在不同的数据结构中对各种各样的数据类型进行排序和自定义排序,并使用Python中两种不同的排序方法。
在这篇教程结束时,你将会知道如何:
1、在数据结构上完成基本的Python排序
2、区分sorted()和.sort()函数
3、基于特定的要求在编码中自定义一个复杂的排序
对于本篇教程而言,你需要对列表、元组以及集合有一个基础的理解。在本篇教程中,将会使用到这些数据结构,并且在这些数据结构上将会进行一些基础的操作。而且,这篇教程使用的是Python3的版本,所以如果你使用的是Python2的版本,输出样例可能会稍微有些差别。
使用sorted()函数排序
在开始使用Python进行排序之前,首先你要了解如何对数值和字符串数据进行排序。
对数值进行排序
你可以使用Python中的sorted()函数对一个列表进行排序。在本例中,定义了一个整数列表,然后调用sorted()函数,可变的numbers作为sorted()函数的参数。
输出结果是一个新的,有序的列表。当打印原始变量时,我们可以知道初始值并没有发生改变。
这个例子展示了sorted()函数四种重要的特性:
1.sorted()函数不需要定义。它是一个内置函数,可以在标准的Python安装中使用。
2.在没有额外的参数的情况下,sorted()函数按照升序对值进行排列,也就是按照从小到大的顺序。
3.原始的numbers不会改变,因为sorted()函数提供了一个新的有序的输出结果,并且不改变原始值的顺序。
4.当sorted()函数被调用时,它会提供一个有序的列表作为返回值。
最后一点意味着列表可以使用sorted()函数,并且输出结果可以立刻赋值给一个变量:
在这个例子中,有一个新的变量numbers_sorted存储了sorted()函数的输出结果。
你可以通过调用help()函数来查看sorted()函数以确认所有的这些观察结果。可选参数key和reverse将在本教程后面介绍:
技术细节:如果你正在从Python2过渡到Python3,并且对它的同名函数非常熟悉,你应该注意Python3中的几个重要变化:
1.Python3中的sorted()函数没有cmp参数。相反,只使用key参数来引入自定义排序逻辑。
2.key和reverse必须作为参数传递,这与Python2不同,在Python2中它们可以作为位置参数传递。
如果需要将Python2的cmp函数转换为key函数,请查看functools.cmp_to_key()。本教程将不介绍使用Python2的任何示例。
元组和集合同样可以使用sorted()函数:
值得注意的是即使输入的是一个集合和元组,输出结果仍然是一个列表,因为sorted()函数根据定义会返回一个新列表。如果返回的对象需要匹配输入类型,则可以将其转化为新类型。如果试图将结果列表转换回集合类型,请注意,按照定义而言,集合是无序的:
正如预料的结果一样,当把结果列表转换为集合时,numbers_set_sorted是无序的。其它的变量,如numbers_tuple_sorted保留了排序后的顺序。
对字符串进行排序
str类型的排序类似于列表和元组等其它可迭代对象。下面的例子展示了sorted()函数如何遍历传递给它的值中的每个字符并在输出中对字符进行排序:
sorted()函数将一个str看作一个列表,并遍历其中的每一个元素。在一个str中,每一个元素都对应着str中的一个字符。sorted()函数以相同的方式对待每一个句子,它会对每个字符包括空格进行排序。
.split()可以改变这个结果并清理输出,.join()可以将所有内容重新连接在一起。我们将会简单介绍输出的特定顺序以及为什么是这样:
在本例中,原句被转换为一个单词列表而不是作为一个str。然后,对该列表进行排序并再次组合形成一个str而不是一个列表。
Python排序的局限性和陷阱
值得注意的是,当你使用Python对非整数类型的值进行排序时,可能会出现一些限制和奇怪的结果。
含有不可比较数据类型的列表无法使用sorted()函数
有些数据类型不能使用sorted()函数相互比较,因为它们太不一样了。如果你试图对一个含有不可比较数据类型的列表使用sorted()函数,Python将会返回一个错误。在本例当中,同一个列表中的None和int不能排序,因为它们是不兼容的:
这个错误说明了为什么Python不能对给定的值进行排序。它试图通过使用小于操作符(
当你试图在不使用sorted()函数的情况下比较两个不可比较值时,Python会抛出相同的类型错误。
如果列表中的值可以比较并且不会抛出类型错误,那么这个列表就可以进行排序。这可以防止对具有本质上不可排序值的迭代器进行排序,并生成可能没有意义的输出。
例如,数字1应该放在单词apple之前吗?然而,如果一个iterable包含整数和字符串的组合,并且它们都是数字,那么可以使用列表将它们转换为可比较的数据类型:
mixed_numbers中的每一个元素都调用了int()函数,可以将任何str值转化为int值。然后,调用sorted()函数就可以成功比较每一个元素,并产生一个排序的结果。
Python还可以隐式地将值转换为另一种类型。在下面的例子中,1<=0的值为False,所以输出结果就是False。作为布尔类型时,数字1可以转化为True,数字0可以转化为False。
即使列表中的元素看起来都不相同,但是它们都可以转化为布尔值(True或False),并使用sorted()函数相互比较:
'A'=='B'和1<=0都被转化为了布尔值False,并且返回了一个有序的输出结果。
这个例子说明了一个排序的重要方面:排序稳定性。在Python当中,当你对相等的值进行排序时,它们将会在输出时保留原本的顺序。即使移动了1的位置,所有其他的值都是相等的,因此它们保持了相对于彼此的原始顺序。在下面的例子中,所有的值都是相等的,并且将会保持它们原本的位置:
如果你观察原始的顺序和输出的顺序,你将会发现1 == 2被转化成了False,并且所有排序后的输出都与原始顺序相同。
当你对字符串排序时,大小写很重要
sorted()函数可以按照升序对字符串列表的值进行排序,默认情况下按照字母顺序:
然而,Python使用每个字符串中第一个字母的Unicode数值来确定升序排序顺序。这意味着sorted()函数不会将名称AL和al看作是一样的。本例将会使用ord()函数返回每个字符串中第一个字母的Unicode数值:
name[0]会返回sorted(names_with_case)中每一个元素的第一个字符,ord()会提供其Unicode数值。即使在字母表中a在M之前,但是M的Unicode数值在a之前,所以排序的结果是M在前。
如果第一个字母是相同的,那么sorted()将使用第二个字符来确定顺序,如果第二个字符是相同的,将会使用第三个字符,以此类推,直到字符串的末尾:
除了最后一个字符外,very_similar_strs的每一个字符都是相同的。使用sorted()函数比较字符串时,由于前五个字符都是相同的,所以输出结果将会根据第六个字符的值来判断。
包含相同值的字符串最终的顺序为从短到长,这是由于较短的字符串没有可以与较长字符串相比较的元素:
最短的字符串'h'在第一位,最长的字符串'hhhhh'在最后一位。
使用含有reverse参数的sorted()函数
正如sorted()函数的help()文档所示,有一个可选的参数reverse,它将根据分配给它的布尔值改变排序。如果reverse = True,那么就会按照降序排列:
排序的逻辑仍然保持不变,这意味着这些名字仍然按照第一个字母排序,但是因为reverse关键字被设置为True,所以输出结果的顺序是相反的。
当reverse关键字设置为False时,顺序将保持升序。之前的任何例子都可以用来检查reverse关键字设置True或Fasle后的结果:
使用含有key参数的sorted()函数
参数key是sorted()函数最强大的组成部分之一。这个参数可以接收一个函数,该函数将作用于排序列表中的每个值,以确定结果的顺序。
以一个简单的例子为例,对一个特定的列表进行排序,我们假设列表中字符串的长度为排序的要求,由短到长。参数key被设置为len()函数,len()函数的功能是返回一个字符串的长度:
最后的结果是一个按字符串顺序从短到长的列表。列表中每个元素的长度是由len()函数确定的,然后按照升序返回。
让我们回到前面的例子,按照第一个字母排序,当出现大小写不同的情况时,key可以通过将整个字符串转换成小写来解决这个问题:
输出值没有被转化为小写,这是因为参数key并没有处理原始列表中的数据。在排序过程中,将对每个元素调用key函数来确定排序顺序,但是输出的仍是原始值。
当使用带有参数key的函数时,有两个主要限制。
第一,传递给key的函数中所需参数的数量必须为1。
下面的例子说明了加法函数的定义,它需要两个参数。当加法函数作为参数key作于用一个数值列表时,它并不能发挥作用,因为它缺少第二个参数。在排序过程中每次调用add()函数,它每次只从列表中接收一个元素:
第二个限制是,带有key的函数必须能够处理迭代序列中的所有值。例如,你有一个以字符串形式表示的数值列表,要对其使用sorted()函数,参数key试图使用int()函数将它们转化为数字。如果迭代序列中的值不能转换为整数,那么该函数将无法产生作用:
每个作为str的数值都可以转换为int,但是four不行。这会引起一个ValueError,其错误解释为four不能被转化为整形,因为它是无效的。
英文原文:https://realpython.com/python-sort/
译者:Lyx