Python应用——自定义排序全套方案
本文始发于个人公众号: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,不用任何其他传参就可以对它进行排序了。
今天的内容虽然难度不大,但是在我们日常编程当中非常常用,经常会出现需要对复杂的对象和内容进行排序的情况,所以希望大家都掌握,因为一定会派上用场的。
今天的文章就是这些,如果觉得有所收获,请顺手扫码点个关注吧,你们的举手之劳对我来说很重要。
Python应用——自定义排序全套方案的更多相关文章
- python中自定义排序函数
Python内置的 sorted()函数可对list进行排序: >>>sorted([36, 5, 12, 9, 21]) [5, 9, 12, 21, 36] 但 sorted() ...
- python之自定义排序函数sorted()
sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面, ...
- Python:Base4(map,reduce,filter,自定义排序函数(sorted),返回函数,闭包,匿名函数(lambda) )
1.python把函数作为参数: 在2.1小节中,我们讲了高阶函数的概念,并编写了一个简单的高阶函数: def add(x, y, f): return f(x) + f(y) 如果传入abs作为参数 ...
- python 自定义排序函数
自定义排序函数 Python内置的 sorted()函数可对list进行排序: >>>sorted([36, 5, 12, 9, 21]) [5, 9, 12, 21, 36] 但 ...
- python自定义排序函数
Python内置的 sorted()函数可对list进行排序: >>>sorted([36, 5, 12, 9, 21]) [5, 9, 12, 21, 36] 但 sorted() ...
- Python自定义排序及我实际遇到的一些题目实例
写在前面,本文主要介绍Python基础排序和自定义排序的一些规则,如果都比较熟悉,可以直接翻到第三节,看下实际的笔试面试题中关于自定义排序的应用. 一.基础排序 排序是比较基础的算法,与很多语言一样, ...
- Python进阶之自定义排序函数sorted()
sorted() .note-content {font-family: "Helvetica Neue",Arial,"Hiragino Sans GB",& ...
- python 顺序读取文件夹下面的文件(自定义排序方式)
我们在读取文件夹下面的文件时,有时是希望能够按照相应的顺序来读取,但是 file_lists=os.listdir()返回的文件名不一定是顺序的,也就是说结果是不固定的.就比如读取下面这些文件,希望能 ...
- Python中自定义类未定义__lt__方法使用sort/sorted排序会怎么处理?
在<第8.23节 Python中使用sort/sorted排序与"富比较"方法的关系分析>中介绍了排序方法sort和函数sorted在没有提供key参数的情况下默认调用 ...
随机推荐
- dfs序 + 树状数组
You are given a tree, it’s root is p, and the node is numbered from 1 to n. Now define f(i) as the n ...
- 一文熟练使用python mock
mock作为python测试模拟对象工具,在单元测试当中使用较多,官方文档详细不够精简,这篇文章介绍mock常用的用法,以下为引用全文,留给自己和有需要的人查阅. https://realpython ...
- 前端开发利器 Web Replay
前端开发人员收到测试发来的 bug 后,通常比较头疼复现的问题. 即使测试人员录了视频,照着一步步操作也不一定能复现,例如bug是与当时的数据相关的. 为了解决这个问题,Firefox 推出了一个重磅 ...
- typedef声明变量也是一种求值过程
前言: 什么叫做:声明变量是求值过程?请看下面的声明, int i; 很简单,声明了个整型变量i,再看如下声明, int *p; 也很简单,立刻反应出来它是指向整型的指针,但是具体如何推倒出来的呢?其 ...
- 从零开始ming的多人联机游戏--游戏客户端(1)六边形地图
打算做的小游戏是一个多人联机的策略类游戏,类似于<文明>那种 游戏的玩法并不确定,开这个坑主要是为了入门后端开发,顺便熟悉下游戏开发 这篇文章使用unity,实现了六边形单元地图的创建.后 ...
- laravel脚手架搭建项目问题之生产环境element-ui组件字体图标显示错误问题
问题描述: 1.element-ui组件使用的是npm安装 2.npm scripe模式开发 3.使用git命令行工具开发 3.开发环境下图标正常显示 4.生产环境下图标显示不正常 分析原因: 图标文 ...
- linux系统iot平台编程阶段总结
1.inline内联函数 在C语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗. 为了解决这个问题,特别的引入了inline修饰符,表示为内联函数. 在使用循 ...
- 响应国家号召,在家撸码之React迁移记
最近这段时间新型冠状病毒肆虐,上海确诊人数每天都在增加,人人提心吊胆,街上都没人了.为了响应国家号召,近期呆在家里撸码,着手将项目迁移到React中,项目比较朴素,是一张线索提交页面,包含表单.图片滚 ...
- CTF--HTTP服务--SQL注入POST参数-注入HTTP报文
开门见山 1. 扫描靶机 2. 对靶机开放端口进行扫描 3. 扫描全部信息 4. 用nikto -host 探测敏感信息 5. 用dirb探测敏感目录 6. 打开一些目录探测出的网页进行查看 7. 再 ...
- Shell之作业控制
命令 含义 jobs 列出所有正在运行的作业 ^Z(Ctrl+z) 暂停作业 bg 启动被暂停的作业 fg 将后台作业调到前台 kill 向指定作业发送kill信号 nohup 忽略所有发送给子命令的 ...