Python 列表有一个内置的
list.sort()
方法可以就地修改列表。
还有一个
sorted()
内置函数,可以从一个可迭代对象构建一个新的排序列表。
这两个排序方法可以自定义比较器
。
list.sort()仅用于列表排序,sorted() 则可以针对任何可迭代对象,总的来说,
sorted() 更方便
。
list.sort() 和 sorted() 都有一个
key
参数,用于指定要调用的函数(这个函数,在进行比较之前要先作用在列表的每个元素上)。
Example:
对复杂对象根据索引排序
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
print(sorted(student_tuples, key=lambda student: student[2])) # sort by age
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
💡我的理解:
lambda student: student[2]分别作用于student_tuples中的每个元素,即('john', 'A', 15),('jane', 'B', 12)和
('dave', 'B', 10),得到了15,12和10,排序即针对15, 12, 10进行, 默认是升序,所以最后排序结果是
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]。
如果需要降序,可以设置reverse参数为True
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
# sort by age,reverse
print(sorted(student_tuples, key=lambda student: student[2], reverse=True))
# [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
当然,排序对象也可以是更复杂的对象,比如:
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),
print(sorted(student_objects, key=lambda student: student.age))# sort by age
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
上述的 key-function 模式非常常见,因此 Python 官方直接提供了一些方便的函数供我们直接调用。比如,operator 模块里有 itemgetter()、attrgetter() 和 methodcaller() 函数。使用这些函数,上面的例子可以更方便完成。
from operator import itemgetter, attrgetter
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
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),
print(sorted(student_tuples, key=itemgetter(2)))
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
print(sorted(student_objects, key=attrgetter('age')))
# [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
operator模块函数还可以进行多级排序,比如先按照成绩排序,然后成绩相同的再按照年龄排序
print(sorted(student_tuples, key=itemgetter(1,2)))
# [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
print(sorted(student_objects, key=attrgetter('grade', 'age')))
# [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
python2中支持 cmp 参数来处理用户指定的比较函数
def numeric_compare(x, y):
return x - y
sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
# [1, 2, 3, 4, 5]
需要解释的是,在使用cmp-function模式时,这里使用的函数(比如上例中的numeric_compare)需要满足一个要求:接受两个参数进行比较,然后返回一个负值表示小于,如果它们相等则返回零,或者返回一个正值表示大于
numeric_compare函数接收两个参数x和y, 返回值是$x-y$, 满足$x<y$时返回负值,相等时返回0,$x>y$时返回正值。默认升序排列,即返回负值时,默认排成x, y,返回正值排成y, x。所以上例最后实现了升序排序。
如果想实现降序则可以稍做修改:
def numeric_compare(x, y):
return y - x
sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
# [5, 4, 3, 2, 1]
python3中移除了cmp参数,只能用key参数。
所以当用户需要用自定义的比较规则时,python3中提供了functools模块的cmp_to_key()函数实现将用户自定义的比较函数映射成key-function模式。
简单点说,cmp_to_key()函数就是个包装器,将你的函数放进去之后,key参数就认识你的函数了。
包装器的样子:(其中的mycmp就是需要传进去的你自己的函数)
def cmp_to_key(mycmp):
'Convert a cmp= function into a key= function'
class K:
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
把你自己的函数(比如reverse_numeric)转换成key-function模式:
sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))
当然,你还可以自己定义运算符,将你要排序的对象变成基本类型数值,然后就可以像其他基本类型比如整型数值一样直接调用sorted()进行排序,无需再指定key=function。
怎么自定义呢?
向上看cmp_to_key()函数中的class k,我们可以自己定义一个类,在里面重写下面这些方法:
__lt__: <
__gt__: >
__ge__: >=
__eq__: ==
__le__: <=
__ne__:!=
还是以上述的Student类为例,实操看看:🍻
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 __lt__(self, other):
return self.grade < other.grade if self.grade != other.grade else self.age < other.age
def __gt__(self, other):
return self.grade > other.grade if self.grade != other.grade else self.age > other.age
def __eq__(self, other):
return self.grade == other.grade and self.name == other.name and self.age == other.age
def __le__(self, other):
return self.grade <= other.grade if self.grade != other.grade else self.age <= other.age
def __ge__(self, other):
return self.grade >= other.grade if self.grade != other.grade else self.age >= other.age
def __ne__(self, other):
return self.grade != other.grade or self.name != other.name or self.age != other.age
student_objects = [
Student('john', 'A', 15),
Student('jane', 'B', 12),
Student('dave', 'B', 10),
print(sorted(student_objects))
# [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
print(sorted(student_objects, reverse=True))
# [('jane', 'B', 12), ('dave', 'B', 10), ('john', 'A', 15)]
说简单点,比较器常用的方法就是下面两类:
⭐第一类:key=function
对于key=function中的function:
1️⃣你可以用lamda公式直接写
2️⃣也可以使用python官方提供的函数(比如上述的itemgetter()等函数)
3️⃣还可以自己定义一个函数,再用cmp_to_key()包装一下
⭐第二类:自定义运算符
在类中重写比较函数,然后直接用常规的比较运算来比较两个值的大小。