Python字典操作详解
1. 引言:什么是字典?
在Python中,字典(Dictionary)是一种极其重要且功能强大的内置数据类型。你可以将它想象成一个现实世界中的字典或者一本通讯录。现实中的单词通过其拼写(键)来查找对应的释义(值),通讯录通过人名(键)来查找对应的电话号码(值)。
Python的字典正是基于这种“键-值”对模型构建的。它是一个无序的(在Python 3.7及之前版本中)或有序的(在Python 3.7+版本中,根据插入顺序)集合,其中每个元素都是一个唯一的键和一个与之关联的值的组合。
核心特性:
键值对存储:数据以 key: value 的形式存储。
键的唯一性:一个字典内,所有的键必须是唯一的。如果你尝试添加一个已存在的键,新值将会覆盖旧值。
键的不可变性:字典的键必须是不可变类型,例如数字、字符串或元组。而列表、字典等可变类型不能作为键。值则可以是任意Python对象,包括另一个字典(这构成了嵌套字典)。
动态性:字典是可变的,意味着你可以在创建后随时添加、删除或修改其中的键值对。
高效查找:字典的内部实现(哈希表)使得通过键来查找值的过程非常高效,其平均时间复杂度接近O(1),无论字典有多大。
正是因为这些特性,字典在Python编程中被广泛应用于需要快速查找、动态关联和组织数据的场景,例如表示JSON对象、配置文件、数据库记录等。
---
2. 字典的创建
创建字典有多种方式,每种方式都适用于不同的场景。
2.1. 使用花括号 {} 创建
这是最直观、最常用的创建方式。你可以在花括号内直接写入键值对,键和值之间用冒号 : 分隔,不同的键值对之间用逗号 , 分隔。
# 创建一个空字典
empty_dict = {}
print(f"空字典: {empty_dict}")
# 创建一个包含几个键值对的字典
student_profile = {
"name": "Alice",
"age": 21,
"major": "Computer Science",
"is_enrolled": True
}
print(f"学生档案: {student_profile}")
# 字典的键可以是数字或元组
complex_keys = {
1: "Number as a key",
(2, 3): "Tuple as a key"
}
print(f"复杂键的字典: {complex_keys}")
代码解释:
* empty_dict = {}:直接使用一对空的花括号初始化了一个空的字典对象。
* student_profile = {...}:在大括号内,我们定义了四个键值对。"name" 是一个字符串键,其对应的值是 "Alice"。"age" 对应的值是整数 21,"is_enrolled" 对应的值是布尔值 True。
* complex_keys = {...}:我们展示了键可以是整数 1,也可以是元组 (2, 3),因为它们都是不可变的。
2.2. 使用 dict() 构造函数
dict() 是一个内置函数,提供了更灵活的字典创建方式。
#### a. 通过关键字参数创建
如果你的键都是合法的标识符(即符合变量命名规则),你可以使用关键字参数的形式来创建字典。
# 使用关键字参数创建字典
course_info = dict(title="Python Programming", credits=4, instructor="Dr. Smith")
print(f"课程信息: {course_info}")
代码解释:
* dict(title="Python Programming", ...):这种方式会自动将 title、credits、instructor 这些字符串作为键,等号后面的值作为字典的值。这使得代码更具可读性。
#### b. 通过包含键值对的可迭代对象创建
你可以将一个序列(如列表、元组)的序列传递给 dict(),其中每个内部序列必须包含两个元素:第一个作为键,第二个作为值。
# 使用包含元组的列表创建字典
data_list = [("name", "Bob"), ("age", 22), ("city", "New York")]
user_info = dict(data_list)
print(f"从列表创建的用户信息: {user_info}")
# 使用包含列表的元组创建字典
data_tuple = (["id", 101], ["status", "active"])
item_info = dict(data_tuple)
print(f"从元组创建的项目信息: {item_info}")
代码解释:
* dict(data_list):dict() 函数会遍历 data_list 列表中的每一个元组。对于第一个元组 ("name", "Bob"),它将 "name" 作为键,"Bob" 作为值;对于第二个元组 ("age", 22),它将 "age" 作为键,22 作为值,以此类推。
#### c. 从另一个字典创建
你可以直接用一个已存在的字典来创建一个新的字典,这相当于创建了一个副本。
original_dict = {"a": 1, "b": 2}
new_dict = dict(original_dict)
print(f"新字典: {new_dict}")
print(f"它们是同一个对象吗? {original_dict is new_dict}") # is 比较对象身份
print(f"它们的内容相等吗? {original_dict == new_dict}") # == 比较内容
代码解释:
* new_dict = dict(original_dict):这行代码会遍历 original_dict 的所有键值对,并用它们来构建一个全新的 new_dict。
* original_dict is new_dict 返回 False,说明 new_dict 是一个独立的新对象,而不是对 original_dict 的引用。
* original_dict == new_dict 返回 True,说明它们包含的键值对是完全相同的。
2.3. 使用字典推导式
字典推导式是一种非常Pythonic且简洁的创建字典的方式,它结合了 for 循环和表达式,可以快速根据其他可迭代对象生成字典。
基本语法:{key_expression: value_expression for item in iterable}
# 1. 从一个列表生成一个字典,值为元素的平方
numbers = [1, 2, 3, 4, 5]
squares_dict = {num: num**2 for num in numbers}
print(f"平方数字典: {squares_dict}")
# 2. 对现有字典进行转换,例如所有值转为大写
original_student = {"name": "charlie", "city": "london"}
transformed_student = {key: value.upper() for key, value in original_student.items()}
print(f"转换后的学生信息: {transformed_student}")
# 3. 带条件的字典推导式,只包含满足条件的键值对
scores = {"Alice": 85, "Bob": 92, "Charlie": 78}
high_achievers = {name: score for name, score in scores.items() if score > 90}
print(f"高分学员: {high_achievers}")
代码解释:
* {num: num2 for num in numbers}:遍历 numbers 列表中的每一个 num,num 作为新字典的键,num2 作为对应的值。
* {key: value.upper() for key, value in original_student.items()}:遍历 original_student 的每一项(.items()方法后面会详述),键 key 保持不变,值 value 通过 .upper() 方法转为大写。
* {name: score for name, score in scores.items() if score > 90}:这是带有 if 条件的推导式。它只会在 score > 90 条件为真时,才将 name: score 这一对加入到新字典中。
---
3. 访问字典中的元素
创建了字典之后,最常用的操作就是获取值。
3.1. 通过键直接访问 []
使用方括号 [] 并在括号内放入键,是最直接的访问方式。
my_dict = {"brand": "Ford", "model": "Mustang", "year": 1969}
# 获取 "model" 对应的值
car_model = my_dict["model"]
print(f"汽车型号: {car_model}")
# 尝试访问一个不存在的键
try:
car_color = my_dict["color"]
except KeyError as e:
print(f"错误发生: {e}")
print("无法找到键 'color',程序中断。")
代码解释:
* car_model = my_dict["model"]:程序在 my_dict 中查找 "model" 这个键,找到了其对应的值 "Mustang",并将其赋给 car_model。
* car_color = my_dict["color"]:这行代码尝试获取一个不存在的键 "color"。Python会立即抛出一个 KeyError 异常,中断程序的正常执行。因此,在使用 [] 访问时,你必须确保键是存在的,或者使用 try...except 块来处理可能出现的错误。
3.2. 使用 .get() 方法安全访问
为了解决 [] 访问不存在的键会报错的问题,字典提供了 .get() 方法。它是一种更安全、更优雅的访问方式。
语法:dictionary.get(key, default_value)
my_dict = {"brand": "Ford", "model": "Mustang", "year": 1969}
# 使用 get() 访问存在的键
car_model = my_dict.get("model")
print(f"使用 get() 获取的汽车型号: {car_model}")
# 使用 get() 访问不存在的键,不提供默认值
car_color = my_dict.get("color")
print(f"访问不存在的 'color',返回: {car_color}") # 默认返回 None
# 使用 get() 访问不存在的键,提供默认值
car_color_default = my_dict.get("color", "Unknown")
print(f"访问不存在的 'color',提供默认值,返回: {car_color_default}")
代码解释:
* my_dict.get("model"):当键 "model" 存在时,.get() 的行为与 [] 完全一样,返回其值。
* my_dict.get("color"):当键 "color" 不存在时,.get() 不会报错,而是会返回一个默认值 None。None 是Python中的一个特殊对象,表示“空”或“无”。
* my_dict.get("color", "Unknown"):这是 .get() 最强大的用法。你可以提供一个自定义的默认值(这里是 "Unknown")。当键不存在时,它会返回这个你提供的默认值,而不是 None。这在逻辑处理中非常有用,可以避免后续对 None 的操作产生意外错误。
---
4. 修改和添加字典元素
字典是可变的,意味着我们可以随时改变它的内容。
4.1. 添加新键值对
如果使用 [] 语法为一个不存在的键赋值,就相当于添加了一个新的键值对。
my_dict = {"name": "David", "age": 30}
print(f"原始字典: {my_dict}")
# 添加一个新的键 "city"
my_dict["city"] = "Shanghai"
print(f"添加 'city' 后的字典: {my_dict}")
# 再次添加一个新的键 "email"
my_dict["email"] = "david@example.com"
print(f"添加 'email' 后的字典: {my_dict}")
代码解释:
* my_dict["city"] = "Shanghai":Python首先检查 "city" 是否是 my_dict 中的一个键。发现不是,于是它就在字典中创建了这个新的键值对。
* 每次为新键赋值,字典都会动态地增长。
4.2. 修改现有键的值
如果使用 [] 语法为一个已经存在的键赋值,就会用新值覆盖旧值。
my_dict = {"name": "David", "age": 30, "city": "Shanghai"}
print(f"修改前的字典: {my_dict}")
# 修改已存在的键 "age" 的值
my_dict["age"] = 31
print(f"修改 'age' 后的字典: {my_dict}")
# 也可以修改其他类型的值,比如把城市名改为全大写
my_dict["city"] = my_dict["city"].upper()
print(f"将 'city' 的值改为大写后: {my_dict}")
代码解释:
* my_dict["age"] = 31:Python发现 "age" 这个键已经存在,于是它将 "age" 关联的值从 30 更新为 31。原来的值 30 就被丢弃了。
* my_dict["city"] = my_dict["city"].upper():我们先获取 "city" 的当前值 "Shanghai",调用其 .upper() 方法得到 "SHANGHAI",然后将这个新字符串重新赋给 "city" 键。
4.3. 使用 .update() 方法批量更新
.update() 方法可以一次性地添加多个键值对,或者修改多个已存在的键的值。它非常灵活,可以接受另一个字典、或者一个包含键值对的可迭代对象。
my_dict = {"name": "Eve", "age": 28}
print(f"原始字典: {my_dict}")
# 1. 使用另一个字典进行更新
update_data_1 = {"age": 29, "city": "Beijing"} # "age"是已存在的键, "city"是新键
my_dict.update(update_data_1)
print(f"用字典更新后: {my_dict}")
# 2. 使用关键字参数进行更新
my_dict.update(job="Engineer", status="Active")
print(f"用关键字参数更新后: {my_dict}")
# 3. 使用包含键值对列表的可迭代对象进行更新
update_data_2 = [("status", "On Leave"), ("department", "R&D")]
my_dict.update(update_data_2)
print(f"用可迭代对象更新后: {my_dict}")
代码解释:
* my_dict.update(update_data_1):update方法会遍历 update_data_1。对于 "age",因为键已存在,所以值被更新为 29。对于 "city",因为键不存在,所以新键值对被添加进来。
* my_dict.update(job="Engineer", ...):这种方式和创建字典时使用的关键字参数类似,它会添加 "job" 和 "status" 这两个新键。
* my_dict.update(update_data_2):这种方式和 dict() 构造函数的用法类似,它会处理 ("status", "On Leave"),更新 status 的值,然后添加 ("department", "R&D") 这个新对。
---
5. 删除字典元素
从字典中移除元素同样有多种方法。
5.1. 使用 del 语句
del 语句可以删除字典中指定的键值对。
my_dict = {"id": 123, "user": "admin", "password": "secret", "active": True}
print(f"原始字典: {my_dict}")
# 删除 "password" 键
del my_dict["password"]
print(f"删除 'password' 后: {my_dict}")
# 尝试删除不存在的键
try:
del my_dict["non_existent_key"]
except KeyError as e:
print(f"错误发生: {e}")
print("删除不存在的键会导致KeyError。")
代码解释:
* del my_dict["password"]:del 是一个Python语句,它会从 my_dict 中永久移除 "password" 这个键以及它关联的值。
* 和 [] 访问一样,如果尝试用 del 删除一个不存在的键,Python会抛出 KeyError。因此在使用 del 时,最好先用 in 操作符检查键是否存在,或者用 try...except 来捕获异常。
5.2. 使用 .pop() 方法
.pop() 方法在删除键值对的同时,会返回被删除的值。这在“取出”一个值并用于后续操作的场景中非常有用。
语法:dictionary.pop(key, default_value)
my_dict = {"item": "Laptop", "price": 1200, "stock": 50}
print(f"原始字典: {my_dict}")
# 删除并获取 "price" 的值
removed_price = my_dict.pop("price")
print(f"被删除的价格是: {removed_price}")
print(f"删除 'price' 后的字典: {my_dict}")
# 尝试 pop() 一个不存在的键,不提供默认值
try:
removed_discount = my_dict.pop("discount")
except KeyError:
print("pop() 不存在的键会报错")
# 尝试 pop() 一个不存在的键,提供默认值
removed_discount = my_dict.pop("discount", 0.0)
print(f"pop() 不存在的键并提供默认值,返回: {removed_discount}")
print(f"字典本身未变: {my_dict}")
代码解释:
* removed_price = my_dict.pop("price"):这行代码做了两件事:1. 从 my_dict 中移除 "price" 这个键。2. 将它关联的值 1200 返回并赋给 removed_price 变量。
* my_dict.pop("discount"):和 del 类似,如果键不存在且没有提供默认值,pop() 会引发 KeyError。
* my_dict.pop("discount", 0.0):当提供了默认值 0.0 后,如果键 "discount" 不存在,pop() 不会报错,也不会修改字典,而是简单地返回这个默认值 0.0。
5.3. 使用 .popitem() 方法
.popitem() 方法会删除并返回字典中的“最后一项”。在Python 3.7及更高版本中,由于字典保持插入顺序,“最后一项”指的是最后被插入的键值对。这在实现一些数据结构(如后进先出栈)时很有用。
# Python 3.7+ 环境
my_dict = {}
my_dict["a"] = 1
my_dict["b"] = 2
my_dict["c"] = 3 # "c"是最后插入的
print(f"当前字典: {my_dict}")
# 弹出最后一项
last_item = my_dict.popitem()
print(f"弹出的项: {last_item}") # 返回一个元组 ('c', 3)
print(f"弹出后的字典: {my_dict}")
# 再次弹出
second_last_item = my_dict.popitem()
print(f"再次弹出的项: {second_last_item}") # 返回 ('b', 2)
print(f"再次弹出后的字典: {my_dict}")
# 尝试从空字典中 popitem()
try:
empty_dict.popitem()
except KeyError as e:
print(f"错误发生: {e}")
代码解释:
* last_item = my_dict.popitem():它移除了最后插入的 "c": 3,并以一个元组 ("c", 3) 的形式返回。
* 如果字典为空,调用 .popitem() 会抛出 KeyError。
5.4. 使用 .clear() 方法
.clear() 方法会移除字典中的所有键值对,使其变成一个空字典。
my_dict = {"data1": 100, "data2": 200}
print(f"清空前的字典: {my_dict}")
print(f"字典ID: {id(my_dict)}")
my_dict.clear()
print(f"清空后的字典: {my_dict}")
print(f"清空后字典ID: {id(my_dict)}") # ID相同,说明是同一个对象
# 对比赋值为空
my_dict_2 = {"data1": 100, "data2": 200}
print(f"\n赋值为空前my_dict_2的ID: {id(my_dict_2)}")
my_dict_2 = {}
print(f"赋值为空后my_dict_2的ID: {id(my_dict_2)}") # ID不同,说明是新的对象
代码解释:
* my_dict.clear():执行后,my_dict 变成了 {}。
* 重要区别:通过 id() 函数我们可以看到,.clear() 是在原地修改字典对象,清空其内容,但字典对象的内存地址没有变。而 my_dict_2 = {} 是将变量 my_dict_2 指向了一个全新的空字典对象,原来的字典对象如果没有其他变量引用它,则可能会被垃圾回收。
---
6. 遍历字典
遍历字典是处理字典数据时非常常见的操作。
6.1. 遍历键
直接对字典进行 for 循环,或者使用 .keys() 方法,默认都是遍历字典的键。在Python 3中,.keys() 返回的是一个视图对象,它比生成键的列表更节省内存。
scores = {"Alice": 95, "Bob": 88, "Charlie": 92}
# 方式一:直接遍历字典
print("--- 遍历键 (方式一) ---")
for name in scores:
print(name)
# 方式二:使用 .keys() 方法 (推荐,意图更清晰)
print("\n--- 遍历键 (方式二) ---")
for name in scores.keys():
print(name)
代码解释:
* for name in scores: 和 for name in scores.keys(): 的效果是完全相同的。循环变量 name 在每次迭代中会依次被赋值为 "Alice", "Bob", "Charlie"。使用 .keys() 可以让代码的意图更加明确,即我们只关心键。
6.2. 遍历值
使用 .values() 方法可以遍历字典中的所有值。
scores = {"Alice": 95, "Bob": 88, "Charlie": 92}
print("--- 遍历值 ---")
for score in scores.values():
print(score)
代码解释:
* for score in scores.values()::循环变量 score 在每次迭代中会依次被赋值为 95, 88, 92。
6.3. 遍历键值对
这是最有用、最常见的遍历方式。使用 .items() 方法可以同时获取键和对应的值。它返回一个包含 (键, 值) 元组的视图对象。
scores = {"Alice": 95, "Bob": 88, "Charlie": 92}
print("--- 遍历键值对 (元组解包) ---")
# 最地道、最推荐的方式
for name, score in scores.items():
print(f"学生: {name}, 分数: {score}")
print("\n--- 遍历键值对 (手动解包) ---")
# 等价的另一种写法
for item in scores.items():
name, score = item # item是一个元组,如
print(f"学生: {name}, 分数: {score}")
代码解释:
* for name, score in scores.items()::这是Python中非常优雅的“元组解包”语法。在每次循环中,.items() 会产生一个元组,比如 ("Alice", 95)。然后这个元组被自动解包,"Alice" 赋值给 name,95 赋值给 score。这种方式代码简洁,可读性高。
---
7. 字典的高级应用和技巧
7.1. 嵌套字典
字典的值可以是任何类型,包括另一个字典。这就构成了嵌套字典,非常适合用来表示具有层次结构的数据。
# 表示一个用户列表,每个用户都有嵌套的地址信息
users = {
"user_001": {
"name": "Frank",
"email": "frank@example.com",
"address": {
"street": "123 Main St",
"city": "Anytown",
"zipcode": "12345"
}
},
"user_002": {
"name": "Grace",
"email": "grace@example.com",
"address": {
"street": "456 Oak Ave",
"city": "Someville",
"zipcode": "67890"
}
}
}
# 访问嵌套字典的值
# 获取 user_001 的城市
city_of_user_001 = users["user_001"]["address"]["city"]
print(f"用户001所在的城市: {city_of_user_001}")
# 修改嵌套字典的值
# 更新 user_002 的邮编
users["user_002"]["address"]["zipcode"] = "67891"
print(f"用户002更新后的邮编: {users['user_002']['address']['zipcode']}")
# 为用户001添加一个新的信息:电话号码
users["user_001"]["phone"] = "555-1234"
print(f"添加电话号码后,用户001的信息: {users['user_001']}")
代码解释:
* users 是一个外层字典,它的键是用户ID,值是另一个包含用户详细信息的内层字典。
* users["user_001"]["address"]["city"]:通过连续的 [] 操作,我们可以一层一层地向内访问,最终获取到 "Anytown"。
* users["user_002"]["address"]["zipcode"] = "67891":同样的多层访问路径也可以用于修改值。
* users["user_001"]["phone"] = "555-1234":为内层字典添加新的键值对,和普通字典操作一样。
7.2. in 操作符检查成员资格
in 操作符可以高效地检查一个键是否存在于字典中。这是检查键存在性的首选方法,比用 .get() 或 try...except 更简洁。
car = {"make": "Honda", "model": "Civic", "year": 2021}
# 检查键是否存在
if "model" in car:
print("字典中包含键 'model'")
else:
print("字典中不包含键 'model'")
if "color" in car:
print("字典中包含键 'color'")
else:
print("字典中不包含键 'color'")
# 注意:in 只检查键,不检查值
if "Civic" in car:
print("字典中包含值 'Civic'") # 这行不会打印
else:
print("in操作符默认检查键,'Civic'不是键。")
# 如何检查一个值是否存在?
if "Civic" in car.values():
print("但是,字典的值中包含 'Civic'。") # 这行会打印
代码解释:
* "model" in car:返回 True,因为 "model" 是 car 的一个键。
* "color" in car:返回 False。
* in 操作符作用于字典本身时,检查的是键。如果你想检查某个值是否存在,需要在 car.values() 这个值视图上进行查找。
7.3. sorted() 函数与字典
虽然字典本身是无序的(在旧版Python中)或按插入顺序有序的(在新版







