list.sort()与sorted()
python 有两个内建排序函数:一个是list.sort(),对调用该函数的list进行原地排序;另一个是sorted(),可以对任意迭代器进行排序,返回list类型的序列。二者有两个区别。
第一,list.sort()没有返回值,只对list原地排序;而sorted()将排序后序列作为新list返回,如下实例:
>>> sorted([5, 2, 3, 1, 4]) [1, 2, 3, 4, 5] >>> 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]
另外,二者都有相同参数:1.key 2.reverse 3.cmp,并且使用方法相同,下面通过sorted()进行详细说明
key参数的使用
基本说明:
key参数指定一个函数,这个函数的实参为每个迭代器的item,经过该函数运算,返回值为每个item中需要进行对比的项(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()函数,在 Python 2.5以后还提供了operator.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)]
operater模块提供的函数,可以进行多级排序. 如下实例, 先以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()函数可以调用方法,操作需要比较的item后,然后对item进行比较。如下实例,str.count()方法对每个item的感叹号进行统计,按照包含感叹号的多少进行排序:
>>> messages = ['critical!!!', 'hurry!', 'standby', 'immediate!!'] >>> sorted(messages, key=methodcaller('count', '!')) ['standby', 'hurry!', 'immediate!!', 'critical!!!']
reverse参数的使用
reverse参数很好理解,如果reverse=True,将以降序排序,如下实例:
>>> 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)]
cmp参数的使用(不推荐)
在python2.4以前,排序没有key参数,所有进行自定义排序,使用的都是cmp参数。而在python 3开始,就没有cmp参数了,另外cmp参数没有key运行得快,所以这里只做简单说明,建议大家尽量使用key参数进行排序
cmp参数值是一个有两个参数的函数,该函数参数的实参也是需要比较的item,通过该函数进行计算,返回的结果如果为负数,证明item1<item2,若为0,证明item1=item2,为正数,证明item1>item2。如下例子:
>>> def numeric_compare(x, y): return x - y >>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare) [1, 2, 3, 4, 5]
刚才说道python3.x不支持cmp参数,如何将python2.x程序迁移到python3.x呢?使用下述方法:
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
>>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric)) [5, 4, 3, 2, 1]
在python2.7以后,functools.cmp_to_key() 可直接通过functools进行调用
另外一些说明
一、为类组成的列表,提供默认的排序方法
在类中将排序中作为key的属性,加入相关比较的方法中,如下:
>>> Student.__eq__ = lambda self, other: self.age == other.age >>> Student.__ne__ = lambda self, other: self.age != other.age >>> Student.__lt__ = lambda self, other: self.age < other.age >>> Student.__le__ = lambda self, other: self.age <= other.age >>> Student.__gt__ = lambda self, other: self.age > other.age >>> Student.__ge__ = lambda self, other: self.age >= other.age >>> sorted(student_objects) [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
一般以实现这六种比较方法为佳,另外可以使用functools.total_ordering()修饰,可以简化代码量
二、key参数的另一种用法
key参数不只依赖被排序的实例,可以使用额外的实例作为key。如下例子:student 成绩存在一个字典中, 此字典可以用于给另一个包含student姓名的list排序::
>>> students = ['dave', 'john', 'jane'] >>> grades = {'john': 'F', 'jane':'A', 'dave': 'C'} >>> sorted(students, key=grades.__getitem__) ['jane', 'dave', 'john']
参考:
http://docs.python.org/2/howto/sorting.html