IT教程 ·

Python运用——自定义排序全套计划

win10打开自带wifi热点共享

本文始发于个人民众号:TechFlow,原创不轻易,求个关注

 

本日的这篇文章和人人聊聊Python当中的排序,和许多高等言语一样,Python封装了成熟的排序函数。我们只需要挪用内部的sort函数,就能够完成排序。然则现实场景当中,排序的运用每每比较庞杂,比方对象范例,当中有多个字段,我们愿望依据指定字段排序,或许是愿望依据多关键字排序,这个时刻就不能简朴的函数挪用来处置惩罚了。

 

字典排序

 

我们先来看下最常见的字典排序的场景,假定我们有一个字典的数组,字典内有多个字段。我们愿望能够依据字典当中的某一个字段来举行排序,我们用现实数据来举个例子:

kids = [
    {'name': 'xiaoming', 'score': 99, 'age': 12},
    {'name': 'xiaohong', 'score': 75, 'age': 13},
    {'name': 'xiaowang', 'score': 88, 'age': 15}
]

这里的kids是一个dict范例的数组,dict当中具有name, score和age三个字段。假定我们当下愿望能够依据score来排序,应当怎么办呢?

关于这个问题,处置惩罚的计划有许多,起首,我们能够运用上一篇文章当中提到的匿名函数来指定排序的。这里的用法和上篇文章优先行列的用法是一样的,我们直接来看代码:

sorted(kids, key=lambda x: x['score'])

在匿名函数当中我们吸收的x是kids当中的元素,也就是一个dict,所以我们想要指定我们愿望的字段,需要用dict接见元素的要领,也就是用中括号来查找对应字段的值。

假如我们愿望依据多关键字排序呢?

起首引见一下多关键字排序,照样用上面的数据打比方。在上面的例子当中,各个kid的score都不一样,所以排序的效果是肯定的。但假如存在两个人的score相称,我愿望岁数小的排在前面,那末应当怎么办呢?我们剖析一下能够发明,原本是依据分数从小到大排序,但有可能会涌现分数相称的状况。这个时刻,我们愿望能够依据在分数相称的状况下来比较岁数,也就是说我们愿望依据两个关键字来排序,第一个关键字是分数,第二个关键字是岁数。

由于Python当中支撑tuple和list范例的排序,也就是说我们能够直接比较[1, 3]和[1, 2]的大小关联,Python会自动一次比较两个数组当中的元素的大小。假如相称就自动今后比较,直到涌现不等或许完毕为止。

邃晓了这点,实在就很好办了。我们只要在匿名函数当中稍稍修正,让它返回的效果增添一个字段即可。

sorted(kids, key=lambda x: (x['score'], x['age']))

 

itemgetter

 

除了匿名函数,Python也有自带的库能够处置惩罚这个问题。用法和匿名函数异常靠近,运用起来稍稍轻易一些。

它就是operator库当中的itemgetter函数,我们直接来看代码:

from operator import itemgetter

sorted(kids, key=itemgetter('score'))

假如是多关键字也能够,传入多个key即可:

sorted(kids, key=itemgetter('score', 'age'))

 

对象排序

 

我们接下来看一下对象的自定义排序,我们起首把上面的dict写成对象:

class Kid:
    def __init__(self, name, score, age):
        self.name = name
        self.score = score
        self.age = age

    def __repr__(self):
        return 'Kid, name: {}, score: {}, age:{}'.format(self.name, self.score, self.age)

为了轻易视察打印效果,我们重载了__repr__要领,能够简朴地将它当作是Java当中的toString要领,如许我们能够指定在print它的时刻的输出效果。

一样,operator当中也供应了对象的排序因子函数,用法上和itemgetter一样,只是名字差别。

from operator import attrgetter

kids = [Kid('xiaoming', 99, 12), Kid('xiaohong', 75, 13), Kid('xiaowang', 88, 15)]

sorted(kids, key=attrgetter('score'))

我们也能够运用匿名函数lambda来完成:

sorted(kids, key=lambda x: x.score)

 

自定义排序

 

到这里还没有完毕,由于依然存在一些问题处置惩罚不了。虽然我们完成了多关键字排序,然则另有一个问题处置惩罚不了,就是排序的次序问题。

我们能够在sorted函数的参数当中传入reverse=True来控制是正序照样倒叙,然则假如我运用多关键字,想要依据某个关键字升序,某个关键字降序怎么办?举个例子,比方说我们想要依据分数降序,岁数升序就没办法经由过程reverse来处置惩罚了,这就是当前处置惩罚不了的问题。

那应当怎么办呢?

这个时刻就需要最终排序杀器上场了,也就是标题当中所说的自定义排序。也就是说我们本身完成一个定义元素大小的函数,然后让sorted来挪用我们这个函数来完成排序。这也是C++和Java等言语的用法。

自定义的函数并不难写,我们随手就来:

def cmp(kid1, kid2):
    return kid1.age < kid2.age if kid1.score == kid2.score else kid1.score > kid2.score

假如看不邃晓,也没紧要,我写成完全版:

def cmp(kid1, kid2):
    if kid1.score == kid2.score:
        return kid1.age < kid2.age
    else:
        return kid1.score > kid2.score

写完了以后,还没有完毕,这个函数是不能直接投入运用的,他和我们之前提到的lambda匿名函数是不一样的。之前的匿名函数只是用来指定字段的,所以我们不能直接将这个函数传递给key,还需要在外面包一层加工处置惩罚才能够。不过这一层处置惩罚函数Python也已经有现成的东西了,我们能够直接挪用,它在functools里,我们来看代码:

from functools import cmp_to_key

sorted(kids, key=cmp_to_key(cmp))

我们来看一下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

我们能够看到,在函数内部,它实在定义了一个类,然后在类当中重载了比较函数,末了返回的是一个重载了比较函数的新的对象。这些__lt__, __gt__函数就是类当中重载的比较函数。比方__lt__是小于的推断函数,__eq__是相称的函数。那末问题来了,我们能不能直接在Kid类当中重载比较函数呢,如许就能够直接排序了。

答案是肯定的,我们固然能够这么办,现实上这也是面向对象当中异常常常使用的做法。比拟于自定义比较函数,我们每每更倾向于在类当中定义好优先级。Python当中完成的要领也很简朴,就是我们手动完成一个__lt__函数,sorted默许会将小的元素排在前面,所以我们只用完成__lt__一个函数就够了。这个函数当中传入的参数是另一个对象,我们直接在函数内里写清晰比较逻辑就好了。返回True示意当前对象比other小,不然比other大。

我们附上完全代码:

class Kid:
    def __init__(self, name, score, age):
        self.name = name
        self.score = score
        self.age = age

    def __repr__(self):
        return 'Kid, name: {}, score: {}, age:{}'.format(self.name, self.score, self.age)

    def __lt__(self, other):
        return self.score > other.score or (self.score == other.score and self.age < other.age)

完成了比较函数以后,我们直接挪用sorted,不必任何其他传参就能够对它举行排序了。

本日的内容虽然难度不大,然则在我们一样平常编程当中异常常常使用,常常会涌现需要对庞杂的对象和内容举行排序的状况,所以愿望人人都控制,由于一定会派上用场的。

本日的文章就是这些,假如以为有所收成,请随手扫码点个关注吧,你们的举手之劳对我来讲很主要。

在Ubuntu上布置一个基于webrtc的多人视频谈天效劳

参与评论