【python基础】基于sorted的排序函数

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中废除了该参数,需要通过参数keycmp_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]
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值