Python—多条件排序

宋正兵 on 2022-07-24

问题描述

有这么一个类代表学生

1
2
3
4
5
6
7
8
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))

一张成绩表中记录了4个学生的成绩和年龄

1
2
3
4
5
6
student_objects = [
Student('john', 'B', 7),
Student('jane', 'A', 11),
Student('dave', 'B', 5),
Student('Amy', 'C', 5)
]

现在宋老师想要对这4个学生的成绩进行排序。

多条件都是升序or降序

学生成绩按照升序排序,如果成绩相同则按照年龄升序排序:

1
2
3
4
5
6
7
8
# 方法1:两次排序,先按次要条件排序
>>> temp_lst = sorted(student_objects, key=lambda x: x.age)
>>> sorted(temp_lst, key=lambda x: x.grade)
[('jane', 'A', 11), ('dave', 'B', 5), ('john', 'B', 7), ('Amy', 'C', 5)]

# 方法2:多条件lambda
>>> sorted(student_objects, key=lambda x: (not x.grade, -x.age))
[('jane', 'A', 11), ('dave', 'B', 5), ('john', 'B', 7), ('Amy', 'C', 5)]

注意,成绩使用字符串进行表示的,Python中字符串按字典序进行排序。

如果想要让学生成绩按照降序排序,成绩相同则按照年龄降序排序:

1
2
3
4
5
6
7
8
# 方法1:两次排序,先按次要条件排序
>>> temp_lst = sorted(student_objects, key=lambda x: x.age, reverse=True)
>>> sorted(temp_lst, key=lambda x: x.grade, reverse=True)
[('Amy', 'C', 5), ('john', 'B', 7), ('dave', 'B', 5), ('jane', 'A', 11)]

# 方法2:多条件lambda
>>> sorted(student_objects, key=lambda x: (x.grade, x.age), reverse=True)
[('Amy', 'C', 5), ('john', 'B', 7), ('dave', 'B', 5), ('jane', 'A', 11)]

现在出现了新的要求,想要对成绩和年龄按照不同的升降顺序进行排列。

多条件有的升序有的降序

学生成绩按照升序排列,对成绩相同的学生按照年龄进行降序排序

1
2
3
4
5
6
7
8
# 方法1:两次排序,先按次要条件排序
>>> temp_lst = sorted(student_objects, key=lambda x: x.age, reverse=True)
>>> sorted(temp_lst, key=lambda x: x.grade)
[('jane', 'A', 11), ('john', 'B', 7), ('dave', 'B', 5), ('Amy', 'C', 5)]

# 方法2:多条件lambda
>>> sorted(student_objects, key=lambda x: (x.grade, -x.age))
[('jane', 'A', 11), ('john', 'B', 7), ('dave', 'B', 5), ('Amy', 'C', 5)]

学生成绩按照降序排列,对成绩相同的学生按照年龄进行升序排序

1
2
3
4
5
6
7
8
# 方法1:两次排序,先按次要条件排序
>>> temp_lst = sorted(student_objects, key=lambda x: x.age)
>>> sorted(temp_lst, key=lambda x: x.grade, reverse=True)
[('Amy', 'C', 5), ('dave', 'B', 5), ('john', 'B', 7), ('jane', 'A', 11)]

# 方法2:多条件lambda
>>> sorted(student_objects, key=lambda x: (not x.grade, x.age))
[('Amy', 'C', 5), ('dave', 'B', 5), ('john', 'B', 7), ('jane', 'A', 11)]

降序处理

细心的读者可能已经发现,在“多条件有的升序有的降序”中,如果用“多条件lambda”的方式进行多条件排序,针对字符串类型的属性,使用not x.grade表示降序,针对数字类型的属性,使用-x.age表示降序。希望大家使用的时候予以重视(不过用错了的话,编译器也会给你报错的:laughing:)。

总结

针对我们所使用的方法1进行解释,为什么可以这样做:

因为Python中的list.sort以及sorted方法,都是稳定排序的,即不会打乱元素的相对顺序。我们可以先对次要条件进行排序,再对主要条件进行排序,达到多条件排序的目的。

在此进行引申,如果我们面对n多个条件的多条件排序如何解决呢?答案照猫画虎,在两个条件的排序模板下进行条件新增就好了。比如方法1那么就根据这n个条件的主次关系进行多次排序,先排最次要的条件,最后排最主要的条件。

方法2的话只需要在lambda表达式中写上n条规则即可,例如key=lambda x: (x.规则1, x.规则2, ..., x.规则n)