开发者社区> 余二五> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

python 排序

简介:
+关注继续查看

Python list内置sort()方法用来排序,也可以用python内置的全局sorted()方法来对可迭代的序列排序生成新的序列。

 

1)排序基础

简单的升序排序是非常容易的。只需要调用sorted()方法。它返回一个新的list,新的list的元素基于小于运算符(__lt__)来排序。

>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

 

你也可以使用list.sort()方法来排序,此时list本身将被修改。通常此方法不如sorted()方便,但是如果你不需要保留原来的list,此方法将更有效。

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

 

另一个不同就是list.sort()方法仅被定义在list中,相反地sorted()方法对所有的可迭代序列都有效。

 

>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]

 

 

2)key参数/函数

从python2.4开始,list.sort()和sorted()函数增加了key参数来指定一个函数,此函数将在每个元素比


较前被调用。 例如通过key指定的函数来忽略字符串的大小写:


>>> sorted("This is a test string from Andrew".split(), key=str.lower)


['a''Andrew''from''is''string''test''This']

 

key参数的值为一个函数,此函数只有一个参数且返回一个值用来进行比较。这个技术是快速的因为key


指定的函数将准确地对每个元素调用。

 

更广泛的使用情况是用复杂对象的某些值来对复杂对象的序列排序,例如:

>>> student_tuples = [
        ('john''A', 15),
        ('jane''B', 12),
        ('dave''B', 10),
]
>>> sorted(student_tuples, key=lambda student: student[2])   # sort by age


[('dave''B', 10), ('jane''B', 12), ('john''A', 15)]

 

同样的技术对拥有命名属性的复杂对象也适用,例如:

>>> 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))
>>> student_objects = [
        Student('john''A', 15),
        Student('jane''B', 12),
        Student('dave''B', 10),
]
>>> sorted(student_objects, key=lambda student: student.age)   # sort by age
[('dave''B', 10), ('jane''B', 12), ('john''A', 15)]

 

3)Operator 模块函数

上面的key参数的使用非常广泛,因此python提供了一些方便的函数来使得访问方法更加容易和快速。operator模块有itemgetter,attrgetter,从2.6开始还增加了methodcaller方法。使用这些方法,上面的操作将变得更加简洁和快速:

>>> from operator import itemgetter, attrgetter


>>> sorted(student_tuples, key=itemgetter(2))


[('dave''B', 10), ('jane''B', 12), ('john''A', 15)]


>>> sorted(student_objects, key=attrgetter('age'))


[('dave''B', 10), ('jane''B', 12), ('john''A', 15)]

 

operator模块还允许多级的排序,例如,先以grade,然后再以age来排序:


>>> sorted(student_tuples, key=itemgetter(1,2))


[('john''A', 15), ('dave''B', 10), ('jane''B', 12)]


>>> sorted(student_objects, key=attrgetter('grade''age'))


[('john''A', 15), ('dave''B', 10), ('jane''B', 12)]

 

4)升序和降序

list.sort()和sorted()都接受一个参数reverse(True or False)来表示升序或降序排序。例如对上面的student降序排序如下:

>>> sorted(student_tuples, key=itemgetter(2), reverse=True)


[('john''A', 15), ('jane''B', 12), ('dave''B', 10)]


>>> sorted(student_objects, key=attrgetter('age'), reverse=True)


[('john''A', 15), ('jane''B', 12), ('dave''B', 10)]

 

5)排序的稳定性和复杂排序

从python2.2开始,排序被保证为稳定的。意思是说多个元素如果有相同的key,则排序前后他们的先后顺序不变。

>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
>>> sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]

注意在排序后'blue'的顺序被保持了,即'blue', 1在'blue', 2的前面。

 

更复杂地你可以构建多个步骤来进行更复杂的排序,例如对student数据先以grade降序排列,然后再以age升序排列。

>>> s = sorted(student_objects, key=attrgetter('age'))     # sort on secondary key


>>> sorted(s, key=attrgetter('grade'), reverse=True)       # now sort on primary key, descending
[('dave''B', 10), ('jane''B', 12), ('john''A', 15)]

 

6)最老土的排序方法-DSU

我们称其为DSU(Decorate-Sort-Undecorate),原因为排序的过程需要下列三步:

第一:对原始的list进行装饰,使得新list的值可以用来控制排序;

第二:对装饰后的list排序;

第三:将装饰删除,将排序后的装饰list重新构建为原来类型的list;

 

例如,使用DSU方法来对student数据根据grade排序:

>>> decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)]
>>> decorated.sort()
>>> [student for grade, i, student in decorated]               # undecorate
[('john''A', 15), ('jane''B', 12), ('dave''B', 10)]

上面的比较能够工作,原因是tuples是可以用来比较,tuples间的比较首先比较tuples的第一个元素,如果第一个相同再比较第二个元素,以此类推。

 

并不是所有的情况下都需要在以上的tuples中包含索引,但是包含索引可以有以下好处:

第一:排序是稳定的,如果两个元素有相同的key,则他们的原始先后顺序保持不变;

第二:原始的元素不必用来做比较,因为tuples的第一和第二元素用来比较已经是足够了。

 

此方法被RandalL.在perl中广泛推广后,他的另一个名字为也被称为Schwartzian transform。

 

对大的list或list的元素计算起来太过复杂的情况下,在python2.4前,DSU很可能是最快的排序方法。但是在2.4之后,上面解释的key函数提供了类似的功能。

 

7)其他语言普遍使用的排序方法-cmp函数

在python2.4前,sorted()和list.sort()函数没有提供key参数,但是提供了cmp参数来让用户指定比较函数。此方法在其他语言中也普遍存在。

 

在python3.0中,cmp参数被彻底的移除了,从而简化和统一语言,减少了高级比较和__cmp__方法的冲突。

 

在python2.x中cmp参数指定的函数用来进行元素间的比较。此函数需要2个参数,然后返回负数表示小于,0表示等于,正数表示大于。例如:

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

 

或者你可以反序排序:

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

 

当我们将现有的2.x的代码移植到3.x时,需要将cmp函数转化为key函数,以下的wrapper很有帮助:

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) < 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
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return K

 

当需要将cmp转化为key时,只需要:

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

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


8)其他注意事项

* 对需要进行区域相关的排序时,可以使用locale.strxfrm()作为key函数,或者使用local.strcoll()作为cmp函数。

 

* reverse参数任然保持了排序的稳定性,有趣的时,同样的效果可以使用reversed()函数两次来实现:

>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
>>> assert sorted(data, reverse=True) == list(reversed(sorted(reversed(data))))

 

其实排序在内部是调用元素的__cmp__来进行的,所以我们可以为元素类型增加__cmp__方法使得元素可比较,例如:

>>> Student.__lt__ = lambda self, other: self.age < other.age
>>> sorted(student_objects)
[('dave''B', 10), ('jane''B', 12), ('john''A', 15)]

 

* key函数不仅可以访问需要排序元素的内部数据,还可以访问外部的资源,例如,如果学生的成绩是存储在dictionary中的,则可以使用此dictionary来对学生名字的list排序,如下:

>>> students = ['dave''john''jane']
>>> newgrades = {'john''F''jane':'A''dave''C'}
>>> sorted(students, key=newgrades.__getitem__)
['jane''dave''john']

 

*当你需要在处理数据的同时进行排序的话,sort(),sorted()或bisect.insort()不是最好的方法。在这种情况下,可以使用heap,red-black tree或treap。


1.字符串列表排序

列表的排序是python内置功能,自身含有sort方法 。如果元素是字符串,会分解成字符比较,而字符的大小是根据字符对应ascii码的大小。

Python代码  收藏代码

>>> str_list = ['spring', 'summer', 'autumn', 'winter']  

>>> str_list.sort()  

>>> print str_list  

['autumn', 'spring', 'summer', 'winter']  

 

Python代码  收藏代码

>>> str_list = ['aB', 'Ab', 'AB', 'ab']  

>>> str_list.sort()  

>>> print str_list  

['AB', 'Ab', 'aB', 'ab']  

 

Python代码  收藏代码

>>> str_list = ['a123', 'B123', 'c123']  

>>> str_list.sort()  

>>> print str_list  

['B123', 'a123', 'c123']  

 如果忽略大小字母,让上面结果显示为:['a123', 'B123', 'c123']。怎么做了?

 

很容易想到DSU用法,decorate-sort-undecorate封装-排序-解封,既简单又快速:

Python代码  收藏代码

>>> str_list = ['c123', 'a123', 'B123']  


>>> temp_list = [(x.lower(), x) for x in str_list]  #decorate

  

>>> temp_list.sort()  #sort  


>>> str_list = [x[1] for x in temp_list]  #undecorate  


>>> print str_list  


['a123', 'B123', 'c123']  

这是一种重要的编程思想,你现在所处的情景,你想要的结果,你要做的就是搭一座桥梁,让两者之间可以联通!这是的桥梁就是temp_list。

 

如果你确定列表中的元素都是字符串,而且你使用的是Python2.4以上版本,那么使用sorted方法来得更简单:

Python代码  收藏代码

>>> str_list = ['c123', 'a123', 'B123']  

>>> sorted(str_list, key=str.lower)  


>>> str_list = sorted(str_list, key=str.lower)  

['a123', 'B123', 'c123']  

 这里我提示一点sorted方法并没有直接作用于目标列表,而是返回一个排序后的列表。如果你知道排序的是unicode对象列表,可以使用key=unicode.lower,或者key = lambda s: s.lower()

 

这里给你补充点知识:

        python有三种字符串类型:str、unicode、basestring。basestring是抽象类,不能实例化,str和unicode是basestring的子类。普通字符串str怎么转换为Unicode字符串?'Hello'+u' '+'World'   这样就行了,连接中有一个是unicode字符串,结果都为unicode字符串。如果你想知道得更详细,我会写另一篇文章。

        lambda 是匿名函数,python中非常有用的一个函数,掌握它对你受益匪浅。

 

这里你会不禁赞叹python的强大,简单而且优美。程序就是一件艺术品,python无疑是一把优质的雕琢刀,而你的地位恰好是雕塑家。成功与否,不是取决于雕琢刀的好坏,而是你雕刻的方法,这正是我们应该学习的!

 

也许上面两种方法你都没有想到,你想到的是:难道不能重新构造比较方法cmp()吗?

Python代码  收藏代码

>>> def mycmp(a, b):  

    return cmp(a.lower(), b.lower())  

  

>>> str_list = ['c123', 'a123', 'B123']  

>>> str_list.sort(mycmp)  

>>> print str_list  

['a123', 'B123', 'c123']  

 不知你发现没有,每两个元素进行比较都会执行两次lower()方法,如果列表中元素为n,那要执行多少次lower()?虽然不推荐这样做,但也给我们提供了一种思路,如果不是字符串列表,而是结构很复杂的数据列表,我们就可以采用这样方法!

2.数字列表排序

Python代码  收藏代码

>>> num_list = [12, 43, 11, 23, 9]  

>>> num_list.sort()  

>>> print num_list  

[9, 11, 12, 23, 43]  

 小数也同样适用

Python代码  收藏代码

>>> num_list = [12, 43.4, 11.5, 23.0, 9.1]  

>>> num_list.sort()  

>>> print num_list  

[9.1, 11.5, 12, 23.0, 43.4]  

 我用的是Python2.7,如果你使用是其他版本,可能会出现不同的结果,如:9.1变成9.100000000001 。这涉及到python中小数是怎么存储的,这里就不多说了!

 

3.字典数据排序

学Java的人都知道Map,而字典就是Python中的“Map”,键值对映射。字典中元素的存储是无序的,怎么对字典数据进行排序?

Python代码  收藏代码

>>> mydict = {"A01":"first", "A14":"second", "Aa1":"third"}  

>>> print mydict  

{'A14': 'second', 'A01': 'first', 'Aa1': 'third'}  

>>> items = mydict.items()  

>>> print items  

[('A14', 'second'), ('A01', 'first'), ('Aa1', 'third')]  

>>> items.sort()  

>>> print items  

[('A01', 'first'), ('A14', 'second'), ('Aa1', 'third')]  

>>> new_list = [value for key, value in items]  

>>> print new_list  

['first', 'second', 'third']  

 前3行说明字典中元素存储是无序的,你构造的字典和实际存储的字典 里元素的顺序并不一样!字典的items()方法能够把字典转换为元组的列表,再对元组列表进行排序,得到自己想要的结果。

 

另一种思路:得到字典key列表,排序后,依次取出value,构造成列表就是你的结果。

Python代码  收藏代码

>>> mydict = {"A01":"first", "A14":"second", "Aa1":"third"}  

>>> keys = mydict.keys()  

>>> keys.sort()  

>>> new_list = [mydict[key] for key in keys] 

 

>>> print new_list  

['first', 'second', 'third']  

 通过映射的方法更有效的执行最后一步

Python代码  收藏代码

>>> mydict = {"A01":"first", "A14":"second", "Aa1":"third"}  

>>> keys = mydict.keys()  

>>> keys.sort()  

>>> new_list = map( mydict.get,keys )  

>>> print new_list  

['first', 'second', 'third']  

 利用lamdba函数,选择根据key或value排序

Python代码  收藏代码

>>> sorted(mydict.items(), key=lambda d:d[0])  

[('A01', 'first'), ('A14', 'second'), ('Aa1', 'third')]  

>>> mydict = {"A01":"first", "A14":"second", "Aa1":"third"}  

>>> sorted(mydict.items(), key=lambda d:d[0])  

[('A01', 'first'), ('A14', 'second'), ('Aa1', 'third')]  

>>> sorted(mydict.items(), key=lambda d:d[1])  

[('A01', 'first'), ('A14', 'second'), ('Aa1', 'third')]  

>>> print mydict  

{'A14': 'second', 'A01': 'first', 'Aa1': 'third'}  

 

4.对象排序

 

Python代码  收藏代码

>>> class Sortobj:  

    a = 0  

    b = ''  

    def __init__(self, a, b):  

        self.a = a  

        self.b = b  

    def printab(self):  

        print self.a, self.b  

  

          

>>> obja = Sortobj(343, 'keen')  

>>> objb = Sortobj(56, 'blue')  

>>> objc = Sortobj(2, 'aba')  

>>> objd = Sortobj(89, 'iiii')  

>>> obj_list = [obja, objb, objc, objd]  

>>> for obj in obj_list:  

    obj.printab()  

  

      

343 keen  

56 blue  

2 aba  

89 iiii  

>>> obj_list.sort(lambda x,y: cmp(x.a, y.a)) #按对象的a属性进行排序  

>>> for obj in obj_list:  

    obj.printab()  

  

      

2 aba  

56 blue  

89 iiii  

343 keen  

>>> obj_list.sort(lambda x,y: cmp(x.b, y.b)) #按对象的b属性进行排序  

>>> for obj in obj_list:  

    obj.printab()  

  

      

2 aba  

56 blue  

89 iiii  

343 keen  

 

5.字典列表排序

元素全是字典的列表,如何排序?其实把上面的方法综合起来就能实现:

Python代码  收藏代码

input = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10},{'name': 'Wang', 'age':10},{'name': 'Ab', 'age':10}]  

print input  

input.sort(lambda x,y : cmp(x['name'], y['name']))   

print input  

如果想降序排列,

Python代码  收藏代码

input.sort(lambda x,y : -cmp(x['name'], y['name']))  










本文转自 chengxuyonghu 51CTO博客,原文链接:http://blog.51cto.com/6226001001/1552973,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《Python编程快速上手——让繁琐工作自动化》——1.5 第一个程序
本节书摘来自异步社区《Python编程快速上手——让繁琐工作自动化》一书中的第1章,第1.5节,作者[美] Al Sweigart,王海鹏 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.5 第一个程序 虽然交互式环境对于一次运行一条 Python指令很好,但要编写完整的Python程序,就需要在文件编辑器中输入指令。
1972 0
+关注
20376
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载