sorted
是Python的内置排序函数,其基本用法为:
sorted(objs, key:key-func, reverse: bool)->List:
其中排序对象objs
是一个可迭代对象,参数key
是一个key function,表示排序所参考的值,参数reverse
表示排序逻辑,默认为False,即升序排列,可设置为True实现自动降序。
1. 常用用法:字典排序
字典排序是sorted
函数最常用的地方,即根据字典内的某个key值的大小进行排序。我们以一个学生考试成绩的例子进行介绍:
# 学生的name、 Chinese score和Maths score字典
students = [
{'name':'Mia', 'Chinese':98, 'Maths':90},
{'name':'Lily', 'Chinese':94, 'Maths':96},
{'name':'tom', 'Chinese':98, 'Maths':93}
]
- 方法一:通过
lambda
函数读取某个key值
# 根据Maths score进行升序排列
sorted(students, key=lambda x:x['Maths'])
- 方法二:通过
itemgetter
函数
from operator import itemgetter
sorted(students, key=itemgetter('Maths'))
2. 字典多个字段的一致性排序
对于多个字段的一致性排序(所谓一致性,即均为升序或降序排序),直接在单字段基础上进行拓展即可:
- 方法一:通过lambda函数同时读取多个key值
sorted(students, key=lambda x:(x['Chinese'],x['Maths']))
- 方法二:通过
itemgetter
函数同时读取多个key值
from operator import itemgetter
sorted(students, key=itemgetter('Chinese','Maths'))
2. 对象排序
sorted
也可以应用于对象排序,这里仍以学生考试成绩的例子进行介绍:
class Student(object):
def __init__(self,name,Chinese,Maths):
self.name = name
self.Chinese = Chinese
self.Maths = Maths
def __repr__(self):
return 'name:{}, Chinese:{}, Maths:{}'.format(self.name, self.Chinese, self.Maths)
students = [Student('Mia',98,90), Student('Lily',94, 96), Student('tom',98,93)]
2.1 基于单个属性的排序
单个属性的排序的排序与字典即为类似,只是改为.
语法糖或attrgetter
函数的方式进行属性值的获取:
- 方法一:示例化对象
.
语法糖
sorted(students, key=lambda x:x.Maths)
- 方法二:
attrgetter
函数
from operator import attrgetter
sorted(students, key=attrgetter('Maths'))
2.2 多个属性的一致性排序
与第一部分字典排序的例子一样,.
语法糖或attrgetter
函数可直接拓展到多个属性的一致性排序。
- 方法一:示例化对象
.
语法糖
sorted(students, key=lambda x:[x.Chinese, x.Maths])
- 方法二:
attrgetter
函数
sorted(students, key=attrgetter('Chinese', 'Maths'))
3. 定制化sorted顺序
在上面的例子中,简单的使用sorted
函数只能实现某个key的升/降序排序或多个keys的一致性排序,那么如何实现更灵活的排序功能呢?即某些key按照升序,而某些key按照降序呢?
比如对上面的Student类,我们要求首先按照Chinese score进行升序排序,当Chinese score相同时,再按照Maths score的降序排列。
3.1 方法一:cmp_to_key
函数
熟悉Python2中sorted的函数想必知道其中cmp
参数就是来解决该问题的,但是在Python3中废除了该参数,需要通过参数key
和cmp_to_key
来实现类似的功能。
步骤一:自定义字典或对象排序的逻辑结构
比如在这个示例里,可做如下定义:
"""
student1, student2分别表示对比的两个Student实例
返回值一般设置为1(其实正数均可)、0或-1(其实负数均可)来分别表示student1>student2(降序),student1=student2(等值),student1<student2(升序)三种排序关系。
"""
def cmp(student1, student2):
if student1.Chinese < student2.Chinese:
return -1
elif student1.Chinese == student2.Chinese:
if student1.Maths < student2.Maths:
return 1
else:
return -1
else:
return 1
值的注意的是sorted
默认为降序排序,因此会捕捉上述函数取值为-1
时的逻辑,即进行对比Chinses score时候会遵循student1.Chinese < student2.Chinese
的顺序(即升序),而在对比Maths时,会遵循student1.Maths >= student2.Maths
(此时返回值为-1,即降序)。
步骤二:利用cmp_to_key
函数封装,传入key
参数
上面定义的排序函数是无法应用于sorted
函数的,可以使用cmp_to_key
将其转换为key
参数所接收的key function。
from functools import cmp_to_key
students = [Student('Mia',98,90), Student('Lily',94, 96), Student('tom',98,93)]
sorted(students, key=cmp_to_key(cmp))
# [name:Lily, Chinese:94, Maths:96,
# name:tom, Chinese:98, Maths:93,
# name:Mia, Chinese:98, Maths:90]
我们再回过头来看下cmp_to_key
函数的内在逻辑:
# cmp_to_key源码
def cmp_to_key(mycmp):
"""Convert a cmp= function into a key= function"""
class K(object):
__slots__ = ['obj']
def __init__(self, obj):
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
__hash__ = None
return K
cmp_to_key
函数返回一个内置类,同时基于定义的cmp
排序逻辑函数重载内置类的__lt__
和__eq__
等魔方方法实现大小的对比。
3.2 方法二:重写类的__lt__
函数
cmp_to_key
函数的核心在于重载对象__lt__
和__eq__
等魔方方法,这提醒我们可以直接对需要对比的类重定义其对应函数来实现排序目的。而sorted
默认的为降序排列,所以在实际操作时只要改写__lt__
便可直接作用于sorted
函数。
class Student(object):
def __init__(self,name,Chinese,Maths):
self.name = name
self.Chinese = Chinese
self.Maths = Maths
def __repr__(self):
return 'name:{}, Chinese:{}, Maths:{}'.format(self.name, self.Chinese, self.Maths)
def __lt__(self, other):
return self.Chinese < other.Chinese or (self.Chinese == other.Chinese and self.Maths >= other.Maths)
students = [Student('Mia',98,90), Student('Lily',94, 96), Student('tom',98,93)]
sorted(students)
# [name:Lily, Chinese:94, Maths:96,
# name:tom, Chinese:98, Maths:93,
# name:Mia, Chinese:98, Maths:90]