由于 Python2 和 Python3 中的排序函数略有区别,本文以Python3为主。
Python 中的排序函数有 sort ,sorted 等,这些适用于哪些排序,具体怎么用,今天就来说一说。
两个函数的区别
这儿直接给出这两个排序函数的区别sort 可以直接改变所排序的变量,而 sorted 不会
sort 是 list 的内建函数,不能用于字典的排序,而 sorted 可以用于列表、元组、字典的排序
函数原型
sort
sort 函数原型如下,其中 L 是列表元素
L.sort(*, key=None, reverse=False)
参数解释:key
key 也是接受一个函数,不同的是,这个函数只接受一个元素,形式如下
def f(a):
return len(a)
key 接受的函数返回值,表示此元素的权值,sort 将按照权值大小进行排序,通常的我们会以 lambda 的形式展现出来,比如
key = lambda x : len(x)reverse
接受False 或者True 表示是否逆序
sorted
sorted 函数原型如下,返回的是一个列表
sorted(iterable, *, key=None, reverse=False)
参数解释:iterable
可以迭代的对象,可以是 list,tuple,dict.items(),dict.keys()或者自定义的类key
和 sort 中的含义相同reverse
和 sort 中的含义相同
实战演练
下面针对不同 Python 类型进行排序。
基础篇
list
# sort 内置函数
a = [14,4,2,19,37,23]
a.sort() #改变原有列表
print(a) #[2, 4, 14, 19, 23, 37]
# sorted 函数
b = [14,4,2,19,37,23]
c = sorted(b) #不改变原有列表
print(b) #[14, 4, 2, 19, 37, 23]
print(c) #[2, 4, 14, 19, 23, 37]在这里,可以看出 sort 是没有返回值的,它会改变原有的列表,而 sorted 需要用一个变量进行接收,它并不会修改原有的列表
tuple
# sorted 函数
b = (14,4,2,19,37,23)
c = sorted(b) #不改变原有列表
print(b) #(14, 4, 2, 19, 37, 23)
print(c) #[2, 4, 14, 19, 23, 37]这里⚠️注意了:对于元组来说,是没有内置函数 sort 的,只能使用 sorted 函数,而且 sorted 函数返回的是 list 类型哦,不能弄错了~~~
dict
这个类型相对复杂一点,多数情况下需要用到 key 参数,但是也不是很复杂,后面的进阶篇会详细讲解
# sorted 函数
a = {"today":2020,"pre":2019,"next":2021}
b = sorted(a.items()) #不改变原有列表
c = sorted(a.items(), key = lambda x:(x[1],x[0]))
print(b) #[('next', 2021), ('pre', 2019), ('today', 2020)]
print(c) #[('pre', 2019), ('today', 2020), ('next', 2021)]如果不加 key 参数,就是默认按照键进行排序,加上 key 参数之后可以按照自己定义的规则来进行排序。
进阶篇
上面的基础篇讲的是最基本的排序方法,但是在实际应用中碰到的情况都比这个复杂怎么办?easy,进阶篇他来了。
自定义规则排序
自定义的排序主要通过参数 key 实现,我们来看看
from operator import itemgetter, attrgetter
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
a = sorted(student_tuples, key = lambda x : x[2]) # 使用 lambda
b = sorted(student_tuples, key = itemgetter(2)) # 使用 itemgetter
c = sorted(student_tuples, key = lambda x : (x[1],x[2])) # 使用 lambda 进行多重排序
d = sorted(student_tuples, key = itemgetter(1,2)) # 使用 itemgetter 进行多重排序
print(a)
print(b)
print(c)
print(d)
结果如下
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
可以看到,key 关键字可以通过 lambda 和 itemgetter 方式实现,既可以实现单一字段的排序,还可以进行多重字段的排序,是不是很省时省力?
自定义类排序
前面讲的都是 Python 的内置类型,那么碰到我们自定义的类型该怎么去排序,其实道理是一样的,假设我们定义的类如下
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),
]
sorted(student_objects, key=lambda student: student.age) # sort by age
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
我们再利用 operator 模块进行排序
from operator import itemgetter, attrgetter
a = sorted(student_objects, key=attrgetter('age'))
b = sorted(student_objects, key=attrgetter('grade', 'age'))
print(a)
print(b)
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]这个过程和前面的内置类型比较几乎一样,差别就在于使用函数分别是 itemgetter, attrgetter,简单的说 itemgetter 是以 index 的形势来获取相对应的值,而 attrgetter 是用 key 来获取相对应的值。具体的区别可以看看 这篇博客。
这里有人就会问了?如果我想先以年龄升序,再以年级降序进行排序那该怎么写?这个就比较复杂了,有两种方法,听我娓娓道来。多重排序函数定义
这种方法定义起来需要有点技巧性,拿上面的学生数据举例吧
def multisort(xs, specs):
for key, reverse in specs:
xs.sort(key=attrgetter(key), reverse=reverse)
return xs
multisort(list(student_objects), (('grade', True), ('age', False)))
#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
看的出来这种定义的技巧相对比较重要,对于非列表的比较和列表的比较大同小异,这里不赘述。使用老方法 cmp 比较函数
这个 cmp 参数到了 Python3 就给取消了不过还是可以通过其他方式给实现,过程繁琐,首先得定义一个比较函数
def cmp_to_key(mycmp):
'Convert a cmp= function into a key= function'
class K:
def __init__(self, obj, *args):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) < 0
def __gt__(self, other):
return mycmp(self.obj, other.obj) > 0
def __eq__(self, other):
return mycmp(self.obj, other.obj) == 0
def __le__(self, other):
return mycmp(self.obj, other.obj) <= 0
def __ge__(self, other):
return mycmp(self.obj, other.obj) >= 0
def __ne__(self, other):
return mycmp(self.obj, other.obj) != 0
return K
这里的 mycmp 函数是自己定义的,我这定义的如下
def cmp(a, b):
if a.age > b.age:
return 1
elif a.age < b.age:
return -1
else:
return a.grade - b.grade
sorted(student_objects, key=cmp_to_key(cmp))
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
这俩结果是一样的,只不过后一种方法代码较长,但是思路简单,前面一种代码短,但是思路复杂一些,推荐前面一种方法,既可以锻炼思维,还能减少代码量。
参考