Python列表具有一个内置的sort()方法和一个sorted()内置函数,该方法可以就地修改列表,而内置函数可以从一个可迭代对象构建一个新的排序列表。有很多方法可以使用它们对数据进行排序,并且在描述它们的各种手册中似乎没有一个单一的中心位置,花点时间彻底搞清楚它。
一、排序基础
简单的升序排序非常容易-只需调用sorted()函数即可。它返回一个新的排序列表:
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
还可以使用列表的list.sort()方法。它就地修改列表(并返回None以避免混淆)。通常,它不如sorted()方便-但是,如果您不需要原始列表,则效率会稍高一些。
>>> a = [5, 2, 3, 1, 4]
>>> a.sort()
>>> a
[1, 2, 3, 4, 5]
这也是sorted()和list.sort()的区别:
1)list.sort()是在原本的数组上改变排序,sorted()是返回一个新的数组
2)list.sort()方法仅为列表定义。相反,sorted()函数接受任何可迭代的。
例如:
>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
从Python 2.4开始,list.sort()和sorted()都添加了一个关键参数,以指定要在进行比较之前在每个列表元素上调用的函数。例如,这是一个不区分大小写的字符串比较:
>>> sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
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))
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)]
利用模块操作键功能
上面显示的键功能模式非常普遍,因此Python提供了便利功能,使访问器功能更容易,更快捷。操作员模块具有itemgetter,attrgetter,并且从Python 2.6开始具有methodcaller函数。
使用这些功能,以上示例变得更加简单和快捷,例如根据某一个下标或者键排序:
>>> student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
>>> 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)]
操作模块功能允许同事对多个下标或者键的排序。例如,先按grade排序然后按age排序:
>>> student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
>>> from operator import itemgetter, attrgetter, methodcaller
>>> 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)]
在下面的示例中,使用了操作模块的第三个函数methodcaller,其中在对每个学生进行排序之前显示了每个学生的weighted_grade(加权成绩):
>>> 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),
]
>>> from operator import itemgetter, attrgetter, methodcaller
>>> [(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=False)
list.sort()和sorted()都接受带有布尔值的反向参数。这用于标记降序排序。例如,要以相反的age(年龄)顺序获取学生数据:
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
>>> 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)]
从Python 2.2开始,保证排序是稳定的。这意味着当多个记录具有相同的键时,将保留其原始顺序。
>>> data = [('red', 1), ('blue', 2), ('red', 2), ('blue', 1)]
>>> from operator import itemgetter, attrgetter, methodcaller
>>> sorted(data, key=itemgetter(0))
>>> [('blue', 2), ('blue', 1), ('red', 1), ('red', 2)]
注意,'blue'的两个记录如何保持其原始顺序,从而确保('blue',2'之前('blue',1)。这个奇妙的属性使你可以通过一系列排序步骤来构建复杂的排序。例如,要将学生数据按年级(grade)降序排序,然后按年龄(age)升序排序,请先对年龄(age)进行排序,然后再按年级(grade)排序:
>>> 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),
]
>>> from operator import itemgetter, attrgetter, methodcaller
>>> 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)]
对列表借助外部字典排序:
>>> students = ['dave', 'john', 'jane']
>>> newgrades = {'john': 'F', 'jane':'A', 'dave': 'C'}
>>> sorted(students, key=newgrades.__getitem__)
['jane', 'dave', 'john']
对列表按age排序:
list1 = [{'name':'a','age':20},{'name':'b','age':30},{'name':'c','age':25}]
sorted(list1,key=lambda x:x['age'],reverse=True)
对字典按key和按value排序:
# 根据字典的key对字典进行排序
dict_1 = {'BBB': 12, 'AAA': 1, 'CCC': 3}
list_1 = sorted(dict_1.items(), key=lambda x: x[0])
sort_dict_1 = {item[0]: item[1] for item in list_1}
print(sort_dict_1)
>>>{'AAA': 1, 'BBB': 12, 'CCC': 3}
# 根据字典的value对字典进行排序
dict_2 = {'BBB': 12, 'AAA': 1, 'CCC': 3}
list_2 = sorted(dict_2.items(), key=lambda x: x[1])
sort_dict_2 = {item[0]: item[1] for item in list_2}
print(sort_dict_2)
>>> {'AAA': 1, 'CCC': 3, 'BBB': 12}