简介
本文对Python的排序方法做一个总结,主要的参照是Andrew Dalke的“Sorting Mini-HOWTO”。
排序基础
排序的基础函数是sort()
和sorted()
,这两者都可以实现排序,但是它们也有一些不同:
sort()
是对原始列表进行排序,排序完成原始列表会变得有序;sorted()
则是会创建一个副本,原始列表不变。sort()
是可变对象(字典、列表)的方法,无参数,无返回值,sort()会改变可变对象,因此无需返回值,sort()
方法是可变对象独有的方法或者属性,而作为不可变对象如元组、字符串是不具有这些方法的,如果调用将会返回一个异常;sorted()
是python的内置函数,并不是可变对象(列表、字典)的特有方法,sorted()
函数需要一个参数(参数可以是列表、字典、元组、字符串),无论传递什么参数,都将返回一个以列表为容器的返回值,如果是字典将返回键的列表。
下面给出几个例子:
>>> 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]
>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
Key参数
Key的值应该是一个函数,来达到排序的目的。直接看例子:
下面这个是按小写字母排序:
>>> sorted("This is a test string from Andrew".split(), key=str.lower) # 按小写字母排序
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
最常用的是使用对象的索引来排序:
>>> 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))
def weighted_grade(self):
return 'CBA'.index(self.grade) / float(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模块
Python中提供了一个Operator模块来集成了一些函数,排序更方面,更快。看例子:
>>> from operator import itemgetter, attrgetter, methodcaller
>>> 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)]
可以灵活的进行多条件排序,比如下面的根据成绩和年龄排序:
>>> 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)]
还可以对之前的类中的函数,加权评分来进行排序:
>>> [(student.name, student.weighted_grade()) for student in student_objects]
[('john', 0.13333333333333333), ('jane', 0.08333333333333333), ('dave', 0.1)]
>>> sorted(student_objects, key=methodcaller('weighted_grade'))
[('jane', 'B', 12), ('dave', 'B', 10), ('john', 'A', 15)]
升序和降序
主要是由reverse
参数来控制,为True时为降序,False为升序:
>>> 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)]
排序稳定性和复杂排序
主要指的是当二次排序时,前一次顺序会被保存下来的:
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
>>> sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
由于这个属性,就可以实行下面的功能:
>>> 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)]