python中的list有个内建的排序方法 list.sort() 但是这个排序方法会修改原list。python中还有一个内置的排序函数 sorted() ,他是根据给定的iterable返回一个新的排过序的list。
下面的文档,我们会给你展示python中排序的各种技术。
基本排序
简单的升续是很容易实现的:只需要调用 sorted() 内建函数。就可以返回一个新的排过序的list:
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
你也可以使用list对象的 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]
Key 函数
从python2.4开始,在 list.sort() 和 sorted() 中,都增加了一个参数:key,key参数是一个函数,python会在排序之前,调用key函数对每个排序的元素进行处理,之后对key返回的处理后元素进行排序。
例如:对字符串进行大小写不敏感的排序:
>>> sorted("This is a test string from Andrew".split(), key=str.lower) ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
key参数必须是个带有一个参数的函数,他的返回值是用来排序的。由于每个排序元素只调用一次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)]
Operator模块的函数
对于大部分排序来说,key函数是通用的,所以python提供了很多简单易用的函数。operator模块中就有 operator.itemgetter() , operator.attrgetter() 等函数;在python2.5后,又加入了 operator.methodcaller() 函数。
如果使用python提供的这些函数,上面的几个例子就会变得更简单:
>>> 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)]
operator.methodcaller() 函数更牛逼,他可以指定一个被排序的元素的某个方法,还可以给该方法指定一个固定的参数。例如,可以使用 str.count() 方法来计算每个排序元素中感叹号的个数,并以此作为排序的优先级:
>>> from operator import methodcaller >>> messages = ['critical!!!', 'hurry!', 'standby', 'immediate!!'] >>> sorted(messages, key=methodcaller('count', '!')) ['standby', 'hurry!', 'immediate!!', 'critical!!!']
升序&降序
在 list.sort() 和 sorted() 中还有一个reverse的boolean参数,用来指定排序的顺序。例如,根据学生数据中年龄的降序排序:
>>> 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)]
排序的稳定性和复杂排序
从python2.2开始,python中的排序都是稳定的。排序是稳定的是指:如果有多个元素有相同的优先级,排序后,拥有相同优先级的元素的相对位置不变。
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)] >>> sorted(data, key=itemgetter(0)) [('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
注意,元素 ('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)]
多重排序请使用python为我们提供的Timsort算法。
参考:
https://docs.python.org/2/howto/sorting.html#sortinghowto