master
/ 6.2.4 列表的排序.ipynb

6.2.4 列表的排序.ipynb @masterview markup · raw · history · blame

Notebook

6.6 列表的排序

python中提供了sort()和reverse()两个方法对列表元素进行排序。

(1)sort()方法

ls.sort(*, key=None, reverse=False)

ls为要排序的列表,ls.sort()方法可以对列表ls中的数据在原地进行排序,默认规则是直接比较元素大小(注意字符串的比较是逐位比较每个字符的大小)。缺省时参数reverse=False,为升序排序;当设置参数reverse=True时,为降序排序。排序后,列表中的元素变为一个有序序列。

In [2]:
L = [7,13,9,82,6,5,4] # 通过赋值创建列表L
print(L)            # 输出列表原始元素['73', '13', '9', '82', '6', '5', '04']
L.sort()            # 比较字符串大小,缺省升序排序
print(L)            # 输出修改过的列表['04', '13', '5', '6', '73', '82', '9'],字符串'13'< '5'
print(L.sort())     # L.sort()无返回值,输出None
[7, 13, 9, 82, 6, 5, 4]
[4, 5, 6, 7, 9, 13, 82]
None
In [3]:
ls = [88,56,95,46,100,77]         # 通过赋值方式创建列表ls
ls.sort(reverse=True)             # 比较数字大小,降序排序
print(ls)                         # 输出修改过的列表[100, 95, 88, 77, 56, 46]
[100, 95, 88, 77, 56, 46]

参数key可以指定排序时应用的规则,不影响列表中元素的值。例如:

In [5]:
L = ['app', 'Apple', 'at', 'AM']
L.sort()  # 按字符串大小排序,依序比较各字符Unicode值
print(L)  # ['AM', 'Apple', 'app', 'at']   # 默认排序规则排序

L.sort(key=str.lower)  # 按转小写后排序
print(L)  # ['AM', 'app', 'Apple', 'at']  # 字符串中的字符按小写顺序排序

L.sort(key=lambda x: x.lower())  # 按转小写后排序
print(L)  # ['AM', 'app', 'Apple', 'at']  # 字符串中的字符按小写顺序排序

L.sort(key=len)  # 按字符串长度排序
print(L)  # ['AM', 'at', 'app', 'Apple']  # 字符串中的字符按小写顺序排序

L.sort(key=lambda x: len(x))  # 按字符串长度排序
print(L)  # ['AM', 'at', 'app', 'Apple']  # 字符串中的字符按小写顺序排序
['AM', 'Apple', 'app', 'at']
['AM', 'app', 'Apple', 'at']
['AM', 'app', 'Apple', 'at']
['AM', 'at', 'app', 'Apple']
['AM', 'at', 'app', 'Apple']

使用的sort方法,不使用key参数时,Python严格按照列表元素中每个数据字符串的ASCII码大小排序,'A' < ‘a’,故所有'A'开头的字符串都在'a'开头的字符串之前。当提供了参数key = str.lower时,Python执行的操作是将key参数得到的str.lower方法,依次应用于列表中的每个数据项,将字符串中所有字符转换为小写字符,并以此结果作为依据,进行统一排序。排序后,列表中的实际数据项仍为原数据项值,并不会受排序参数key得到的函数和方法影响,key参数得到的函数或方法,只作为排序依据使用。

内置排序函数sorted(iterable)可以对可迭代对象进行排序,根据 iterable 中的项返回一个新的已排序列表,创新新的排序后的对象

In [ ]:
sorted(iterable, /, *, key=None, reverse=False)

具有两个可选参数,星号*表示它们都必须指定为关键字参数
key 指定带有单个参数的函数,用于从 iterable 的每个元素中提取用于比较的键 (例如 key=str.lower)。 默认值为 None (直接比较元素)。

reverse 为一个布尔值。 如果设为 True,则每个列表元素将按反向顺序比较进行排
内置的 sorted() 确保是稳定的,如果一个排序确保不会改变比较结果相等的元素的相对顺序就称其为稳定的 序。

In [5]:
L = ['app', 'Apple', 'at', 'AM']
print(sorted(L))  # ['AM', 'Apple', 'app', 'at']   默认排序规则排序
print(sorted(L, key=str.lower))  # ['AM', 'app', 'Apple', 'at']  字符串中的字符按小写顺序排序
print(sorted(L, key=lambda x: x.lower()))  # ['AM', 'app', 'Apple', 'at']  字符串中的字符按小写顺序排序
print(sorted(L, key=len))  # ['at', 'AM', 'app', 'Apple'] ,  字符串中的字符按长度排序 
print(sorted(L, key=lambda x: len(x)))  # ['at', 'AM', 'app', 'Apple']  字符串中的字符按长度排序
['AM', 'Apple', 'app', 'at']
['AM', 'app', 'Apple', 'at']
['AM', 'app', 'Apple', 'at']
['at', 'AM', 'app', 'Apple']
['at', 'AM', 'app', 'Apple']

(2)reverse()方法
ls.reverse()方法的作用是不比较元素大小,直接将列表ls中的元素逆序。

In [6]:
L = ['73','13','9','82','6','5','04']  #通过赋值创建元素为字符串的列表L
print(L)           # 输出列表原始元素['73', '13', '9', '82', '6', '5', '04']
L.reverse()        # 将列表元素逆序
print(L)           # 输出修改过的列表['04', '5', '6', '82', '9', '13', '73']

ls = [88,56,95,46,100,77]        # 通过赋值方式创建元素为整数的列表ls
ls.reverse()                     # 将列表元素逆序
print(ls)                        # 输出修改过的列表[77, 100, 46, 95, 56, 88]
['73', '13', '9', '82', '6', '5', '04']
['04', '5', '6', '82', '9', '13', '73']
[77, 100, 46, 95, 56, 88]

这两种方法都是原地操作,直接修改了原始的列表中的数据,有时,我们只希望在输出时进行排序或逆序输出,不改变列表中的原始数据的顺序。此时,可以使用Python的内置函数sorted()和reversed()。这两个函数都是只返回排序或逆序的对象的结果,而不对原列表进行任何修改,也就是说,不会改变列表中元素原始的顺序。 注意:使用这两个内置函数时,列表要放在括号中作为函数的参数。reversed(ls)产生的是一个逆序的对象,需要用list()函数将其转为列表才可以输出。

In [7]:
L = ['73','13','9','82','6','5','04']  # 通过赋值创建列表L
print(L)        # 输出列表原始元素    ['73', '13', '9', '82', '6', '5', '04']
print(sorted(L))# 将列表元素排序输出  ['04', '13', '5', '6', '73', '82', '9']
print(L)        # 列表L元素顺序不变  ['73', '13', '9', '82', '6', '5', '04']

ls = [88,56,95,46,100,77]       # 创建列表ls,值为: [88, 56, 95, 46, 100, 77]
print(reversed(ls))   # <list_reverseiterator object at 0x00000126AC5CA6E0>

print(list(reversed(ls)))     # 将列表元素逆序并转为列表输出 [77, 100, 46, 95, 56, 88]
print(ls)             # 列表ls元素顺序不变            [88, 56, 95, 46, 100, 77]
['73', '13', '9', '82', '6', '5', '04']
['04', '13', '5', '6', '73', '82', '9']
['73', '13', '9', '82', '6', '5', '04']
<list_reverseiterator object at 0x7f12bc126750>
[77, 100, 46, 95, 56, 88]
[88, 56, 95, 46, 100, 77]

练一练

实例 5.1 成绩统计分析 有10名同学的python课程成绩分别为:94, 89, 96, 88, 92, 86, 69, 95, 78, 85,利用列表分析成绩,输出平均值、最高的3个成绩和最低的3个成绩、成绩中位数。 分析: 平均成绩可以将所有成绩加和再除以10获得,最高和最低成绩需要排序后输出前后各3个成绩,中位数也需要先排序再求取。如果原列表顺序不需要保留,可以使用列表的sort()方法进行排序。

In [2]:
# 利用列表存储数据,统计平均成绩、中位数、最高三个成绩和最低三个成绩
scores = [94, 89, 96, 88, 92, 86, 69, 95, 78, 85]
scores.sort()          # 对成绩列表排序,默认升序,scores 中原来顺序丢失

print(sum(scores)/10)  # 利用sum()函数对序列元素求和计算平均成绩
print('最高3个成绩为:', scores[-1: -4: -1]) # 后面3个成绩,步长-1表示逆序
print('最低3个成绩为:', scores[0: 3])       # 前面3个成绩,顺序输出,升序
count = len(scores)# 取得成绩个数

if count % 2 == 0: # 当列表元素数目为偶数时,中位数为中间两个数据的算术平均数
    median = (scores[count // 2 -1] + scores[count // 2 ])/2
else:              # 当列表元素数目为奇数时,中位数即列表中间的数字
    median = scores[ count // 2 ]
    
print('成绩中位数是:{:.2f}'.format(median))
print(scores)   
# sort()方法使原列表排序发生变化,[69, 78, 85, 86, 88, 89, 92, 94, 95, 96]
87.2
最高3个成绩为: [96, 95, 94]
最低3个成绩为: [69, 78, 85]
成绩中位数是:88.50
[69, 78, 85, 86, 88, 89, 92, 94, 95, 96]

很多时候,希望原列表中的顺序可以被保留下来,这时,可以使用Python内置函数sorted()在输出时进行排序:

In [3]:
scores = [94, 89, 96, 88, 92, 86, 69, 95, 78, 85]

print(sum(scores)/10)
print('最高3个成绩为:',sorted(scores)[-1: -4: -1])#输出时排序,不影响原列表
print('最低3个成绩为:', sorted(scores)[0: 3])

count = len(scores)   # 取得成绩个数
if count % 2 == 0: # 当列表元素数目为偶数时,中位数为中间两个数据的算术平均数
    median = (sorted(scores)[count // 2 -1] + sorted(scores)[count // 2 ])/2
else:              # 当列表元素数目为奇数时,中位数即列表中间的数字
    median = sorted(scores)[ count // 2 ]
    
print('成绩中位数:{:.2f}'.format(median))
print(scores)   
# sorted()函数不改变原列表顺序[94, 89, 96, 88, 92, 86, 69, 95, 78, 85]
87.2
最高3个成绩为: [96, 95, 94]
最低3个成绩为: [69, 78, 85]
成绩中位数:88.50
[94, 89, 96, 88, 92, 86, 69, 95, 78, 85]

拓展一下这个问题,当成绩数据保存在文件中时,如何对成绩进行分析? 文件中的数据如下: 王龙 94 张龙 89 梁龙 96 杨林 88 刘雪 92 魏琴 86 杜鑫 69 刘君 95 王娜 78 周华 85 读文件中的数据的应用非常多,可以先将文件中的数据读取为列表形式,再用上述方法对列表中的数据进行分析和统计。

In [4]:
scores = []                   # 创建空列表
with open('images/ch6/6.1 score.txt','r',encoding='utf-8') as data:
   for line in data:           # 遍历文件对象
      line = line.strip()      # 去除行末的换行符
      line = line.split()      # 根据空白字符将字符串切分为列表
      line = int(line[1])      # 索引方法获取列表中序号为1的元素
      scores.append(line)      # 将其转为整数附加到列表末尾
print(scores)                  # 输出列表
[94, 89, 96, 88, 92, 86, 69, 95, 78, 85]
In [17]:
# 字符串的处理方法可以连续使用,上述代码可以简化为下面形式
scores = []                      # 创建空列表
with open('images/ch6/6.1 score.txt','r',encoding='utf-8') as data:
   for line in data:             # 遍历文件对象
      scores.append(int(line.strip().split()[1]))      
print(scores)                    # 输出列表
# [94, 89, 96, 88, 92, 86, 69, 95, 78, 85]

实例 5.2 二维列表的排序 列表score = [[ 'Angle', '0121701100106',99], [ 'Jack', '0121701100107',86], [ 'Tom', '0121701100109',65], [ 'Smith', '0121701100111', 100], ['Bob', '0121701100115',77], ['Lily', '0121701100117', 59]] 每个列表元素的三个数据分别代表姓名、学号和成绩,请分别按姓名、学号和成绩排序输出。 分析:二维列表的排序可以用lambda函数指定排序关键字,而且可以指定多个排序关键字,将用于确定排序的关键字按顺序放在同一个括号中,放于lambda关键字的冒号后面即可。

In [5]:
# 二维列表元素排序以及根据多个字段用不同的排序规则进行排序
score = [[ 'Angle', '0121701100106',99], [ 'Jack', '0121701100107',86], [ 'Tom', '0121701100109',77], [ 'Smith', '0121701100111', 100], ['Bob', '0121701100115',77], ['Lily', '0121701100117', 59]]
print('按姓名排序')
print(sorted(score, key=lambda x:x[0])) # 按元素中序号为0的元素“姓名”排序
print('按学号排序')
print(sorted(score, key=lambda x:x[1])) # 按元素中序号为1的元素“学号”排序
print('优先按成绩排序再按姓名排序')
print(sorted(score, key=lambda x:(x[2],x[0])))  
#先按成绩升序排序,成绩相同时再按学号升序排序
 
按姓名排序
[['Angle', '0121701100106', 99], ['Bob', '0121701100115', 77], ['Jack', '0121701100107', 86], ['Lily', '0121701100117', 59], ['Smith', '0121701100111', 100], ['Tom', '0121701100109', 77]]
按学号排序
[['Angle', '0121701100106', 99], ['Jack', '0121701100107', 86], ['Tom', '0121701100109', 77], ['Smith', '0121701100111', 100], ['Bob', '0121701100115', 77], ['Lily', '0121701100117', 59]]
优先按成绩排序再按姓名排序
[['Lily', '0121701100117', 59], ['Bob', '0121701100115', 77], ['Tom', '0121701100109', 77], ['Jack', '0121701100107', 86], ['Angle', '0121701100106', 99], ['Smith', '0121701100111', 100]]
按姓名排序 [['Angle', '0121701100106', 99], ['Bob', '0121701100115', 77], ['Jack', '0121701100107', 86], ['Lily', '0121701100117', 59], ['Smith', '0121701100111', 100], ['Tom', '0121701100109', 77]] 按学号排序 [['Angle', '0121701100106', 99], ['Jack', '0121701100107', 86], ['Tom', '0121701100109', 77], ['Smith', '0121701100111', 100], ['Bob', '0121701100115', 77], ['Lily', '0121701100117', 59]] 优先按成绩排序再按姓名排序 [['Lily', '0121701100117', 59], ['Bob', '0121701100115', 77], ['Tom', '0121701100109', 77], ['Jack', '0121701100107', 86], ['Angle', '0121701100106', 99], ['Smith', '0121701100111', 100]]

元素为字符串的二维列表排序

对列表[('hubei', 'wuhan'), ('hubei', 'huangshi'), ('hubei', 'huanggang'), ('hunan', 'shangsha')]进行排序,先输出默认排序结果;再先按城市名升序排序,城市名相同时按省名升序排序;再先按省名降序排序,省名相同时按城市名升序排序;

对于多个排序关键字都是字符串类型的,排序一个升序一个降序时,可以将字符依次转为unicode编码做排序依据,以值的正负表示降序或升序。

In [1]:
city = [('hubei', 'wuhan'), ('hubei', 'huangshi'), ('hubei', 'huanggang'), ('hunan', 'shangsha')]

print(sorted(city))
print(sorted(city, key=lambda x: (x[1], x[0])))
print(sorted(city, key=lambda x: ([-ord(i) for i in x[0]], [ord(i) for i in x[1]])))
In [ ]: