其它综合

Python:如何排序(sort)

我爱IT资讯库   2021/02/24

一、前言

python的列表(list)有两个排序方法:

一种是内建的list.sort()方法,可以直接改变列表的内容:

gt;gt;gt; list1 = [9,8,7,6,5]
gt;gt;gt; list1.sort()
gt;gt;gt; list1
[5, 6, 7, 8, 9]

另一个内建函数是sorted(),它的特点是不改变原列表的内容,而是根据一个可迭代对象建立一个新的列表:

gt;gt;gt; list2 = [4,3,2,1]
gt;gt;gt; list3 = sorted(list2)
gt;gt;gt; list2
[4, 3, 2, 1]
gt;gt;gt; list3
[1, 2, 3, 4]

二、基础排序

一个最简单的升序排序是非常容易的:直接调用sorted()函数就可以了,它返回一个新的列表:

gt;gt;gt; sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

也可以使用列表本身的方法list.sort()去排序。它会改变list的内容,然后返回none作为执行的结果,以避免混淆。一般来说它没有sorted()那么方便,但是如果你不需要原来的列表的话,使用它在性能上会有轻微的提升。

gt;gt;gt; a = [5, 2, 3, 1, 4]
gt;gt;gt; a.sort()
gt;gt;gt; a
[1, 2, 3, 4, 5]

另一个区别就是,list.sort()方法只能用于列表,相对的,sorted()函数则适用于所有的可迭代对象,如:

gt;gt;gt; sorted({1: ‘d‘, 2: ‘b‘, 3: ‘b‘, 4: ‘e‘, 5: ‘a‘})
[1, 2, 3, 4, 5]

三、key函数

从python2.4开始,无论是list.sort()还是sorted()都增加了一个key参数,指定一个在进行比较之前在每个列表元素上调用的函数。

例如,以下就是大小写不敏感的字符串比较:

gt;gt;gt; sorted("this is a test string from andrew".split(), key=str.lower)
[‘a‘, ‘andrew‘, ‘from‘, ‘is‘, ‘string‘, ‘test‘, ‘this‘]

key参数对应的值,必须是这样一个函数:接受一个参数然后返回一个用来排序的键。用这种技术来排序的速度是非常快的,因为key函数恰好被每一个输入记录调用一次。

一种常用的模式是,在对复杂对象进行排序的时候,使用这个对象的索引作为排序的键,例如:

gt;gt;gt; student_tuples = [
...     (‘john‘, ‘a‘, 15),
...     (‘jane‘, ‘b‘, 12),
...     (‘dave‘, ‘b‘, 10),
... ]
gt;gt;gt; sorted(student_tuples, key=lambda student: student[2])   # sort by age
[(‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12), (‘john‘, ‘a‘, 15)]

用对象的命名属性也是一样的,例如:

gt;gt;gt; 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))

gt;gt;gt; student_objects = [
...     student(‘john‘, ‘a‘, 15),
...     student(‘jane‘, ‘b‘, 12),
...     student(‘dave‘, ‘b‘, 10),
... ]
gt;gt;gt; sorted(student_objects, key=lambda student: student.age)   # sort by age

[(‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12), (‘john‘, ‘a‘, 15)]

四、使用operator模块的函数

上面提到的key函数在排序中是很常用的,因此python本身提供了很多很方便的函数,让创建访问器函数变得更快、更容易。operater模块提供的函数有operator.itemgetter(),operator.attrgetter(),另外从python2.5开始新增了operator.methodcaller()函数。

使用这些函数,上面的例子可以变得更简单和更快:

gt;gt;gt; from operator import itemgetter, attrgetter
gt;gt;gt; sorted(student_tuples, key=itemgetter(2))
[(‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12), (‘john‘, ‘a‘, 15)]
gt;gt;gt; sorted(student_objects, key=attrgetter(‘age‘))
[(‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12), (‘john‘, ‘a‘, 15)]

operator模块提供的几个函数允许多重级别的排序。例如,根据grade和age进行排序:

gt;gt;gt; sorted(student_tuples, key=itemgetter(1,2))
[(‘john‘, ‘a‘, 15), (‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12)]
gt;gt;gt; sorted(student_objects, key=attrgetter(‘grade‘, ‘age‘))
[(‘john‘, ‘a‘, 15), (‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12)]

operator.methodcaller()函数会在每个被排序的对象上执行一个固定参数的方法调用,例如,str.count()方法可以通过统计在每个消息中感叹号的数量,来计算消息的优先度:

gt;gt;gt; from operator import methodcaller
gt;gt;gt; messages = [‘critical!!!‘, ‘hurry!‘, ‘standby‘, ‘immediate!!‘]
gt;gt;gt; sorted(messages, key=methodcaller(‘count‘, ‘!‘))
[‘standby‘, ‘hurry!‘, ‘immediate!!‘, ‘critical!!!‘]

五、升序和降序

list.sort()和sorted()都接受一个reverse参数,这个参数的取值是一个布尔值,用来标记是否降序排序。例如,用age字段降序排列去获取学生信息:

gt;gt;gt; sorted(student_tuples, key=itemgetter(2), reverse=true)
[(‘john‘, ‘a‘, 15), (‘jane‘, ‘b‘, 12), (‘dave‘, ‘b‘, 10)]

gt;gt;gt; sorted(student_objects, key=attrgetter(‘age‘), reverse=true)
[(‘john‘, ‘a‘, 15), (‘jane‘, ‘b‘, 12), (‘dave‘, ‘b‘, 10)]

六、排序稳定性及复杂排序

从python2.2版本开始,排序都是稳定的,也就是说如果有两个记录他们的排序键相等,则排序前后他们的相对位置是固定的。

gt;gt;gt; data = [(‘red‘, 1), (‘blue‘, 1), (‘red‘, 2), (‘blue‘, 2)]
gt;gt;gt; sorted(data, key=itemgetter(0))
[(‘blue‘, 1), (‘blue‘, 2), (‘red‘, 1), (‘red‘, 2)]

观察可以得知两个排序键同样是rsquo;bluersquo;的记录,他们的原始顺序在排序前后没有改变,(lsquo;bluersquo;, 1)保证在(lsquo;bluersquo;, 2)前面。

这种极妙的属性可以让你用一系列排序的步骤去创建一种复杂的排序。例如,用grade字段降序,age字段升序去排序学生数据,age字段先排,grade字段后排。

gt;gt;gt; s = sorted(student_objects, key=attrgetter(‘age‘))     # sort on secondary key
gt;gt;gt; sorted(s, key=attrgetter(‘grade‘), reverse=true)       # now sort on primary key, descending
[(‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12), (‘john‘, ‘a‘, 15)]

python的timsort算法可以高效率地进行多重排序,因为它能很好的利用数据集中已经存在的有序序列。

七、使用decorate-sort-undecorated老方法

这个习语是以它的三个步骤而被命名为decorate-sort-undecorate(装饰-排序-去装饰)的:

例如,使用dsu方法用学生数据中的grade字段对其进行排序:

gt;gt;gt; decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)]
gt;gt;gt; decorated.sort()
gt;gt;gt; [student for grade, i, student in decorated]               # undecorate
[(‘john‘, ‘a‘, 15), (‘jane‘, ‘b‘, 12), (‘dave‘, ‘b‘, 10)]

在这里,元组是按照字典序进行比较的,首先第一项被比较,如果他们相等则对第二项进行比较,以此类推。

不是在所有的情况下都需要在被装饰的列表中包含下标i,但是包含它会有两个好处:

让排序稳定mdash;mdash;如果两个项拥有一样的键,那么他们在排序列表中的顺序不会改变。

原始的项不需要是可对比的,因为被装饰的元组的顺序最多被前面两个项决定。举个例子,原始列表可以包含不能直接排序的复数。

这个习语的另一个名字是schwartzian transform,以randal l. schwartz进行命名,因为他用per语言推广了这种方法。

对于大型的列表,以及那些计算对比关系的代价很昂贵的列表来说,在python的2.4版本之前,dsu几乎是排序这类列表的最快的方法。但是在2.4及之后的版本,key函数就提供了一样的功能了。

八、使用cmp参数老方法

在这篇文章中提到的很多设计,其实都是python2.4或之后的版本才给出的。在此之前,无论sorted()函数还是list.sort()方法都没有不带参的调用方式。相反的,所有py2.x版本支持cmp参数,来处理用户指定的对比函数。

在python3中,cmp参数是完全被移除了(这是对简化和统一这门语言作出的努力的一部分,另外也消除了富比较(rich comparisons )和__cmp()__魔术方法的冲突)。

在python2中,sort()允许指定一个可选的函数,这个函数作为比较的用途被调用。这个函数必须带两个被用来比较的参数,然后返回一个值,如果小于是负数,如果相等是0,如果大于则返回正数。例如,我们可以这么做:

gt;gt;gt; def numeric_compare(x, y):
...     return x - y
gt;gt;gt; sorted([5, 2, 4, 1, 3], cmp=numeric_compare) 
[1, 2, 3, 4, 5]

或者你也可以反转对比的顺序:

gt;gt;gt; def reverse_numeric(x, y):
...     return y - x
gt;gt;gt; sorted([5, 2, 4, 1, 3], cmp=reverse_numeric) 
[5, 4, 3, 2, 1]

从python2.x导入代码到3.x的时候,如果你使用这种比较函数的时候,代码会报错,然后你就需要把它转为一个key函数了。以下的包装器可以让这个转化过程变得更简单:

def cmp_to_key(mycmp):
    ‘convert a cmp= function into a key= function‘
    class k(object):
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) lt; 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) gt; 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) lt;= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) gt;= 0
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return k

想转化成一个key函数,直接包装旧的对比函数就可以了:

gt;gt;gt; sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))
[5, 4, 3, 2, 1]

在python2.7中,functools.cmp_to_key()函数已经被增加到functools模块中了。

九、其他

对于浮点数的排序,使用locale.strxfrm()作为key函数,或者locale.strcoll()作为对比函数。

reverse参数依旧保持排序的稳定性。有趣的是,这个效果可以用内建函数reversed()函数两次进行模拟:

gt;gt;gt; data = [(‘red‘, 1), (‘blue‘, 1), (‘red‘, 2), (‘blue‘, 2)]
gt;gt;gt; standard_way = sorted(data, key=itemgetter(0), reverse=true)
gt;gt;gt; double_reversed = list(reversed(sorted(reversed(data), key=itemgetter(0))))
gt;gt;gt; assert standard_way == double_reversed
gt;gt;gt; standard_way
[(‘red‘, 1), (‘red‘, 2), (‘blue‘, 1), (‘blue‘, 2)]

为一个类创建一种标准的排序顺序,只需要增加合适的富比较函数即可:

gt;gt;gt; student.__eq__ = lambda self, other: self.age == other.age
gt;gt;gt; student.__ne__ = lambda self, other: self.age != other.age
gt;gt;gt; student.__lt__ = lambda self, other: self.age lt; other.age
gt;gt;gt; student.__le__ = lambda self, other: self.age lt;= other.age
gt;gt;gt; student.__gt__ = lambda self, other: self.age gt; other.age
gt;gt;gt; student.__ge__ = lambda self, other: self.age gt;= other.age
gt;gt;gt; sorted(student_objects)
[(‘dave‘, ‘b‘, 10), (‘jane‘, ‘b‘, 12), (‘john‘, ‘a‘, 15)]

对于一般用途的比较,一般的方法建议定义全部六个富对比操作符。现在functools.total_ordering() 类装饰器让这一切变得更加容易实现。

key函数不一定要直接基于被排序的对象。key函数同样可以访问外部的资源。举个例子,如果学生的年级被存储在一个字典之中,那它就可以用来为另一个单独的的学生的名字组成的列表进行排序:

gt;gt;gt; students = [‘dave‘, ‘john‘, ‘jane‘]
gt;gt;gt; grades = {‘john‘: ‘f‘, ‘jane‘:‘a‘, ‘dave‘: ‘c‘}
gt;gt;gt; sorted(students, key=grades.__getitem__)
[‘jane‘, ‘dave‘, ‘john‘]

以上内容翻译自python2.7.15文档,sorting how to。

(完)

python:如何排序(sort)

原文地址:https://www.cnblogs.com/harrymore/p/9460532.html




热门内容

boost库在工作(5)作用域智能指针scoped_ptr之四

第二种情况,主要就是使用在调用异常抛出的函数的地方。如下面的例子: //异常抛出的函数,适合使用智能指针... ...

Error:couldnotopen`C:\ProgramFiles\Java\jre6\lib\i386\jvm.cfg'

昨天刚过情人节!哈哈,好久没记录学习内容了,今天在房子闲着没事重新安装jdk,按道理很简单的; 第一步下载jdk; ...

第49周二

晚上总结下今天,主要是在完善用户需求文档,同时看了jquery相关的操作技巧,主要是想学习jquery源码,在知乎jqu ...

C++ #include 和 using std::string

今天,偶尔写了一个小小的程序,关于字符串问题程序。 比如,我想连续打印用户输入的字符串。 #include&l... ...

Struts Hibernate Spring 经典面试题

Hibernate工作原理及为什么要用? 原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建Ses... ...

SAP Performance浅析

本文来源于:http://scnblogs.techweb.com.cn/tcsapbw/archives/106... ...

数据库MySQL与xls文件的互导

      最近的一个项目需要将xls表导入到MySQL数据库中和将MySQL数据表导出到... ...

Head-of-Line Blocking (线头阻塞)

Head of Line (HOL) Blocking 产生的原因: 概念:队列的首个packet由于它的目的... ...

Python set的高效利用

python set的应用   ... ...

CollectionFrameWork

collectionframework如下: collection ├list │├linkedlist │├array ...

JAVA访问修饰符构造函数的问题(转)

java访问修饰符 构造函数的问题 java访问修饰符-限定符总结(类比c#) java访问修饰符--------- ...

PAT甲1004CountingLeaves【dfs】

1004counting leaves(30 分) a family hierarchy is usually p ...

171.[LeetCode]Excel Sheet Column Number

题目: Related to question Excel Sheet Column Title ... ...

在.NET中获取一台电脑名,IP地址及当前用户名

在.NET中获取一台电脑名,IP地址及当前用户名是非常简单,以下是我常用的几种方法,如果大家还有其他好的方法,可以回复一... ...
sun directory server

sun directory server

Sun One Directory Server(LDAP)安装和调整指南   ... ...
黑客讲故事:攻下隔壁女生路由器后,我都做了些什么

黑客讲故事:攻下隔壁女生路由器后,我都做了些什么

路由器被蹭网后,我有被黑的风险吗? Evi1m0,来自知道创宇,邪红色信息安全组织创始人 其实这个问题可以... ...

oracle 10g for redhat5

解压文件 解压文件命令: unzip 10201_database_linux32.zip ... ...

什么是I2C协议?

I2C协议是单片机与其它芯片常用的通讯协议,由于只需要两根线,所以很好使用。 一. I2C协议技术性能:&nb... ...

[USACO15DEC]最大流MaxFlow

题目:洛谷p3128。 题目大意:一棵n个点的树,每次将两个节点最短路径所覆盖的所有节点的流量加1。问你最后流量最大的节 ...

kde4.1 alpha1

KDE Project Ships First Alpha of KDE 4.1 KDE Commun... ...

oracle 函数 和 优化

sql语句中,如果where条件里面含有not, !=, <> ,null ,则即使该字段建有索引,也... ...
09年中国互联网企业市值排名

09年中国互联网企业市值排名

这是一个最坏的时代,也是一个最好的时代。自07年底美国次贷危机以来,全球经济发生了巨大的变化。股票市场也随之跌荡起... ...

用于发送UDP消息的SQL Server 扩展存储过程

下载源文件 13.1 kb介绍我希望能够发布 sql server 表更新,因此修改了微软的示例扩展存储过程 xp_he ...

Cache与Fetch(二)

这两天一直百思不得其解的问题终于解决了,这个问题如下: 通过HQL:“select distinct forumGr... ...

[转]小规模低性能低流量网站设计原则

作者: Fenng 网址: http://www.dbanotes.net/arch/small_site... ...

divcss圆角

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transit... ...

Kindle Paperwhite 越狱/加字体/支持PDF、EPUB、DjVu、FB2、CHM和DOC文档

0. 升级 官网固件升级:http://www.amazon.com/gp/help/customer/displ... ...
购物网第二阶段总结笔记3:用户注册模块

购物网第二阶段总结笔记3:用户注册模块

事先工作: 【1】建立用户表:  分析静态页面的用户信息,可以得出用户表所需的字段,建立用户表S... ...

pythonbottleweb框架简介

bottle 是一个快速,简单,轻量级的 python wsgi web 框架。单一文件,只依赖 python 标准库 ...

Android Log介绍

android.util.Log常用的方法有以下5个:Log.v() ,Log.d() ,Log.i() ,Log.w(... ...