
本文将详细介绍如何在python中对嵌套字典的内部字典进行排序,特别是当需要将空列表作为值的数据项置于排序结果的末尾时。我们将利用`operator.not_`作为排序键,结合`sorted()`函数和字典的`update()`方法,实现高效且灵活的自定义排序逻辑,确保数据结构满足特定业务需求。
在处理复杂的数据结构时,我们经常会遇到需要对嵌套字典中的元素进行排序的场景。一个常见的需求是将特定类型的元素(例如空列表)放置在排序结果的末尾。本教程将以一个具体的学生测试成绩字典为例,详细讲解如何实现这种自定义排序。
原始数据结构
假设我们有一个表示学生信息的字典,其中包含一个名为 "tests" 的内部字典。这个 "tests" 字典的键是测试名称(如 "test1", "test2"),值是包含成绩、等级和时间的列表。某些测试可能还没有数据,因此其值是空列表。
data = {
"Student Id": {
"Name": "student name",
"tests": {
"test1": ["mark", "grade", "time"],
"test2": ["mark", "grade", "time"],
"test3": [], # 这是一个空列表
"test4": ["mark", "grade", "time"]
}
}
}我们的目标是对 "tests" 字典进行排序,使得所有非空列表的测试项排在前面,而空列表的测试项(例如 "test3")排在最后。
期望的排序结果
经过排序后,我们期望 "tests" 字典的结构如下(字典在Python 3.7+中保持插入顺序):
立即学习“Python免费学习笔记(深入)”;
{
"Student Id": {
"Name": "student name",
"tests": {
"test1": ["mark", "grade", "time"],
"test2": ["mark", "grade", "time"],
"test4": ["mark", "grade", "time"],
"test3": [] # 空列表被移到了最后
}
}
}核心原理:利用 operator.not_ 进行排序
Python中的布尔值 False 在排序时通常被视为小于 True。在Python中,空列表、空字符串、None、数字 0 等都被认为是“假值”(falsy)。任何非空的列表、非零数字等都被认为是“真值”(truthy)。
operator.not_ 是 not 运算符的函数版本。它的作用是:
- 如果输入是真值(例如非空列表),not_ 返回 False。
- 如果输入是假值(例如空列表 []),not_ 返回 True。
因此,当我们使用 key=operator.not_ 对列表进行排序时:
- 非空列表(真值)会得到一个 False 的排序键。
- 空列表(假值)会得到一个 True 的排序键。
由于 False 在排序时小于 True,所以非空列表将排在空列表之前,从而实现了将空列表置于末尾的需求。
实现步骤与代码
为了实现上述排序,我们将采取以下步骤:
- 获取目标嵌套字典(即 "tests" 字典)。
- 使用 sorted() 函数对该字典的值进行排序,排序键设置为 operator.not_。
- 将原始字典的键与排序后的值重新组合,并更新原始字典。
from operator import not_
# 原始数据
data = {
"Student Id": {
"Name": "student name",
"tests": {
"test1": ["mark", "grade", "time"],
"test2": ["mark", "grade", "time"],
"test3": [],
"test4": ["mark", "grade", "time"]
}
}
}
# 1. 获取目标字典
tests = data['Student Id']['tests']
# 2. 对字典的值进行排序,空列表置后
# tests.values() 获取所有值
# key=not_ 使得空列表的排序键为True,非空列表为False
sorted_values = sorted(tests.values(), key=not_)
# 3. 将原始字典的键与排序后的值重新组合,并更新原始字典
# zip(tests, sorted_values) 将原始键与排序后的值一一对应
# tests.update(...) 使用新的键值对更新字典,从而改变值的顺序
tests.update(zip(tests, sorted_values))
# 打印排序后的结果
import json
print(json.dumps(data, indent=4))代码解释:
- from operator import not_: 导入 not_ 函数。
- tests = data['Student Id']['tests']: 这一行获取了我们需要排序的 tests 字典的引用。
- sorted_values = sorted(tests.values(), key=not_): 这是排序的核心。tests.values() 返回一个视图对象,包含字典中所有的值。sorted() 函数对这些值进行排序,key=not_ 指定了排序的依据。如前所述,空列表会被 not_ 映射为 True,非空列表映射为 False,因此排序结果是所有非空列表在前,所有空列表在后。
- tests.update(zip(tests, sorted_values)): 这一步非常巧妙。
- tests (在 zip 中作为第一个参数) 提供了原始字典的键("test1", "test2", "test3", "test4"),它们的顺序在Python 3.7+中是保持的。
- sorted_values 提供了已经排序好的值(["mark", "grade", "time"], ["mark", "grade", "time"], ["mark", "grade", "time"], [])。
- zip(tests, sorted_values) 将这两个序列打包成一系列的键值对元组:("test1", ["mark", "grade", "time"]), ("test2", ["mark", "grade", "time"]), ("test3", ["mark", "grade", "time"]), ("test4", [])。注意,这里的键仍然是原始的键,但是它们被重新关联到了排序后的值。
- tests.update(...) 使用这些新的键值对来更新 tests 字典。由于字典在Python 3.7+中保持插入顺序,update() 会根据提供的键值对的顺序来更新或插入元素,从而实现了对字典值的重新排序,同时保持了键的原始语义。
注意事项与总结
- Python版本兼容性: 上述方法依赖于Python 3.7+中字典保持插入顺序的特性。如果是在Python 3.6或更早版本中运行,字典的键序可能不会被保留,导致最终结果不确定。
- 原地修改: tests.update() 操作会直接修改原始字典 data 中的 "tests" 字典,是原地修改。如果需要保留原始字典,应先进行深拷贝。
- 通用性: 这种利用 operator.not_ 进行排序的方法不仅限于空列表,还可以应用于任何布尔值(True/False)、数字 0/非零、空字符串/非空字符串等假值/真值的排序场景。
- 自定义更复杂的排序: 如果需要根据列表的长度、特定元素的值等更复杂的条件进行排序,可以将 key 参数设置为一个自定义的 lambda 函数或普通函数,该函数接收字典的值并返回一个用于比较的值。
通过本文的讲解,您应该已经掌握了如何利用 operator.not_ 结合 sorted() 和 update() 方法,对嵌套字典中的值进行灵活排序,尤其是在需要将空列表等假值置于末尾的场景。这种技术在数据清洗、数据展示等多个方面都具有实用价值。









