字典
# 字典(Dictionary)
# 1. 字典的核心概念
字典(Dictionary)是 Python 中一种极其灵活的数据结构,它以 键-值对 (key-value pair) 的形式存储数据。
你可以把它想象成一本真实的字典或电话簿:
- 键 (Key): 就像字典里的单词或电话簿里的人名。键必须是唯一的且不可变的。最常用的键是字符串和数字。
- 值 (Value): 就像单词的释义或人名的电话号码。值可以是任意类型的数据,比如数字、字符串、列表,甚至另一个字典。
为什么键必须是不可变的? 字典的内部工作原理依赖于对键进行“哈希(hash)”计算,以实现快速查找。只有不可变的对象(如字符串、数字、元组)才能保证其哈希值永不改变。如果键是可变的(如列表),它的内容一旦改变,哈希值也会变,字典就无法再找到它了。
字典的核心特性:
- 无序 (Unordered): 在 Python 3.7 之前,字典是完全无序的。从 Python 3.7 开始,字典会记住元素的插入顺序。但我们通常不应该依赖这个顺序,而是通过键来访问数据。
- 可变 (Mutable): 字典在创建后,可以随时添加、修改或删除键值对。
- 通过键访问: 字典通过键来查找对应的值。这个操作的平均时间复杂度是 O(1),意味着无论字典有多大,查找速度都几乎是恒定的,非常快。
# 2. 创建字典
# 2.1 使用花括号 {}
这是最常见的创建方式。
# 创建一个空字典
empty_dict = {}
print(f"空字典: {empty_dict}") # 输出: 空字典: {}
# 创建一个包含键值对的字典
person = {
"name": "Alice",
"age": 25,
"city": "New York"
}
print(f"个人信息字典: {person}") # 输出: 个人信息字典: {'name': 'Alice', 'age': 25, 'city': 'New York'}
2
3
4
5
6
7
8
9
10
11
# 2.2 使用 dict()
构造函数
dict()
提供了多种灵活的创建方式。
# 1. 通过关键字参数创建 (键必须是合法的变量名)
person_alt = dict(name="Bob", age=30)
print(f"关键字参数创建: {person_alt}") # 输出: 关键字参数创建: {'name': 'Bob', 'age': 30}
# 2. 通过包含元组的列表创建
items = [("fruit", "apple"), ("color", "red")]
from_list = dict(items)
print(f"从元组列表创建: {from_list}") # 输出: 从元组列表创建: {'fruit': 'apple', 'color': 'red'}
# 3. 通过 zip 函数将两个列表合并成字典
keys = ["a", "b", "c"]
values = [1, 2, 3]
from_zip = dict(zip(keys, values))
print(f"从 zip 对象创建: {from_zip}") # 输出: 从 zip 对象创建: {'a': 1, 'b': 2, 'c': 3}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3. 访问字典元素:查
场景对比:
[]
vs.get()
- 场景一:你确信键一定存在。 比如,处理一个结构固定的数据。此时使用
[]
更直接,代码也更简洁。- 场景二:你不确定键是否存在。 比如,处理用户输入或外部API返回的数据。此时必须使用
.get()
,并提供一个合理的默认值,以防止程序因KeyError
而崩溃。
# 3.1 使用方括号 []
这是最直接的访问方式。如果键不存在,会立即抛出 KeyError
错误。
person = {"name": "Alice", "age": 25}
print(f"姓名: {person['name']}") # 输出: 姓名: Alice
# 尝试访问一个不存在的键会引发错误
# print(person['gender']) # 这行代码会取消注释后会抛出 KeyError: 'gender'
2
3
4
5
6
# 3.2 使用 .get()
方法 (推荐)
.get(key, default=None)
是更安全、更灵活的访问方式。
person = {"name": "Alice", "age": 25}
print(f"年龄: {person.get('age')}") # 输出: 年龄: 25
print(f"性别: {person.get('gender')}") # 输出: 性别: None (键不存在,返回默认的 None)
print(f"城市: {person.get('city', 'Unknown')}") # 输出: 城市: Unknown (键不存在,返回你指定的默认值)
2
3
4
5
# 4. 添加与修改字典元素:增 & 改
字典的添加和修改操作使用同一种语法,非常直观。
person = {"name": "Alice", "age": 25}
# 添加新键值对 (因为 'city' 键不存在)
person["city"] = "New York"
print(f"添加城市后: {person}") # 输出: 添加城市后: {'name': 'Alice', 'age': 25, 'city': 'New York'}
# 修改已存在的键的值 (因为 'age' 键已存在)
person["age"] = 26
print(f"修改年龄后: {person}") # 输出: 修改年龄后: {'name': 'Alice', 'age': 26, 'city': 'New York'}
2
3
4
5
6
7
8
9
# 使用 .update()
合并字典
.update()
方法可以将另一个字典或一个包含键值对的可迭代对象(如元组列表)合并到当前字典中。
profile = {"name": "Charlie", "gender": "Male"}
contact_info = {"email": "charlie@example.com", "name": "Charles"} # 注意 'name' 键重复
profile.update(contact_info) # contact_info 中的键值对会覆盖 profile 中的
print(f"更新后的个人资料: {profile}")
# 输出: 更新后的个人资料: {'name': 'Charles', 'gender': 'Male', 'email': 'charlie@example.com'}
2
3
4
5
6
# Python 3.9+ 的合并运算符
在 Python 3.9 及以上版本中,你可以使用更简洁的 |
和 |=
运算符来合并字典。
dict_a = {'a': 1, 'b': 2}
dict_b = {'b': 3, 'c': 4}
# |: 创建一个新字典,不修改原始字典
merged_dict = dict_a | dict_b
print(f"使用 | 合并: {merged_dict}") # 输出: 使用 | 合并: {'a': 1, 'b': 3, 'c': 4}
print(f"dict_a 未改变: {dict_a}") # 输出: dict_a 未改变: {'a': 1, 'b': 2}
# |=: 原地更新字典
dict_a |= dict_b
print(f"使用 |= 更新: {dict_a}") # 输出: 使用 |= 更新: {'a': 1, 'b': 3, 'c': 4}
2
3
4
5
6
7
8
9
10
11
# 5. 删除字典元素:删
方法 | 描述 | 键不存在时 | 返回值 |
---|---|---|---|
del my_dict[key] | 直接删除键值对 | 抛出 KeyError | 无 |
my_dict.pop(key) | 删除键值对 | 抛出 KeyError | 被删除的值 |
my_dict.pop(key, default) | 安全地删除 | 返回 default | 被删除的值或 default |
my_dict.popitem() | 删除最后一项 | 抛出 KeyError | 被删除的 (key, value) 元组 |
# 5.1 del
关键字
person = {"name": "Alice", "age": 25, "city": "New York"}
del person["city"]
print(f"删除 city 后: {person}") # 输出: 删除 city 后: {'name': 'Alice', 'age': 25}
2
3
# 5.2 .pop()
方法
person = {"name": "Alice", "age": 25}
removed_age = person.pop("age")
print(f"被删除的年龄: {removed_age}") # 输出: 被删除的年龄: 25
print(f"pop('age') 后: {person}") # 输出: pop('age') 后: {'name': 'Alice'}
# 安全地 pop
removed_city = person.pop("city", "Not Found")
print(f"尝试删除 city: {removed_city}") # 输出: 尝试删除 city: Not Found
2
3
4
5
6
7
8
# 5.3 .popitem()
方法
person = {"name": "Alice", "age": 25, "city": "New York"}
last_item = person.popitem()
print(f"被删除的最后一项: {last_item}") # 输出: 被删除的最后一项: ('city', 'New York')
print(f"popitem() 后: {person}") # 输出: popitem() 后: {'name': 'Alice', 'age': 25}
2
3
4
# 5.4 .clear()
方法
person = {"name": "Alice", "age": 25}
person.clear()
print(f"clear() 之后: {person}") # 输出: clear() 之后: {}
2
3
# 6. 遍历字典
# 6.1 遍历键 (Key)
直接 for key in my_dict:
是最常用和最简洁的方式。
person = {"name": "Alice", "age": 25}
for key in person:
print(f"键: {key}")
# 输出:
# 键: name
# 键: age
2
3
4
5
6
# 6.2 遍历值 (Value)
使用 .values()
方法获取所有值的视图。
for value in person.values():
print(f"值: {value}")
# 输出:
# 值: Alice
# 值: 25
2
3
4
5
# 6.3 遍历键值对 (Item)
使用 .items()
方法是同时获取键和值的最佳方式。它返回包含 (key, value)
元组的视图。
# 使用元组解包,代码非常优雅
for key, value in person.items():
print(f"{key} -> {value}")
# 输出:
# name -> Alice
# age -> 25
2
3
4
5
6
# 7. 字典视图 (Views)
.keys()
, .values()
, .items()
返回的不是列表,而是特殊的视图对象。
视图对象的关键特性是动态性:如果原始字典发生改变,视图会立即反映这些变化。这使得它们非常节省内存,因为不需要每次都复制数据。
person = {"name": "Alice", "age": 25}
keys_view = person.keys()
print(f"添加前的视图: {keys_view}") # 输出: 添加前的视图: dict_keys(['name', 'age'])
# 修改原始字典
person["city"] = "New York"
# 视图会自动更新,无需重新获取
print(f"添加后的视图: {keys_view}") # 输出: 添加后的视图: dict_keys(['name', 'age', 'city'])
2
3
4
5
6
7
8
9
10
# 8. 字典推导式 (Dictionary Comprehension)
与列表推导式类似,字典推导式可以用一行代码优雅地创建字典。
基本语法: {key_expression: value_expression for item in iterable}
# 创建一个数字及其平方的字典
squares = {x: x**2 for x in range(1, 6)}
print(f"平方数字典: {squares}") # 输出: 平方数字典: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 从列表中筛选数据
fruits = ["apple", "banana", "cherry"]
fruit_lengths = {f: len(f) for f in fruits if len(f) > 5}
print(f"长度大于5的水果: {fruit_lengths}") # 输出: 长度大于5的水果: {'banana': 6, 'cherry': 6}
# 键值互换
original_dict = {'a': 1, 'b': 2}
swapped_dict = {v: k for k, v in original_dict.items()}
print(f"键值互换后: {swapped_dict}") # 输出: 键值互换后: {1: 'a', 2: 'b'}
2
3
4
5
6
7
8
9
10
11
12
13
# 9. 更多高级方法
# 9.1 .setdefault(key, default)
这个方法非常有用,它用一行代码解决了“检查-设置-获取”的常见模式。
# 传统写法: 需要 if 判断
data = {}
if 'items' not in data:
data['items'] = []
data['items'].append('item1')
# 使用 setdefault 的优雅写法
data = {}
# 如果 'items' 键不存在,则创建一个空列表[]并设为其值,然后返回这个列表
# 如果 'items' 键已存在,则直接返回其对应的值(已存在的列表)
data.setdefault('items', []).append('item1')
data.setdefault('items', []).append('item2')
print(f"使用setdefault构建列表: {data}") # 输出: 使用setdefault构建列表: {'items': ['item1', 'item2']}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 9.2 dict.fromkeys(iterable, value=None)
这是一个类方法,用于快速创建一个新字典,其键来自一个可迭代对象,所有键共享同一个值。
keys = ["name", "age", "city"]
# 创建一个所有值都为 "unknown" 的字典
default_person = dict.fromkeys(keys, "unknown")
print(f"默认字典: {default_person}")
# 输出: 默认字典: {'name': 'unknown', 'age': 'unknown', 'city': 'unknown'}
# 如果不提供值,默认为 None
none_dict = dict.fromkeys(keys)
print(f"值为None的字典: {none_dict}")
# 输出: 值为None的字典: {'name': None, 'age': None, 'city': None}
2
3
4
5
6
7
8
9
10
# 10. 字典的复制:浅拷贝与深拷贝
警告: 字典的复制是初学者极易出错的地方。如果你的字典的值包含了列表或另一个字典等可变类型,请务必小心。
# 10.1 浅拷贝 (.copy()
或 dict()
)
只复制字典的顶层,如果值是可变对象(如列表、字典),则只复制其引用。修改嵌套对象会影响到原始字典。
original = {"user": "Alice", "data": [1, 2]}
shallow_copy = original.copy()
shallow_copy["data"].append(3) # 修改嵌套列表
print(f"原始字典: {original}") # 输出: 原始字典: {'user': 'Alice', 'data': [1, 2, 3]} (被意外修改!)
print(f"浅拷贝字典: {shallow_copy}") # 输出: 浅拷贝字典: {'user': 'Alice', 'data': [1, 2, 3]}
2
3
4
5
6
7
# 10.2 深拷贝 (copy.deepcopy()
)
递归地复制所有层级的元素,创建一个与原始对象完全独立的新字典。
import copy
original = {"user": "Alice", "data": [1, 2]}
deep_copy = copy.deepcopy(original)
deep_copy["data"].append(3) # 修改嵌套列表
print(f"原始字典: {original}") # 输出: 原始字典: {'user': 'Alice', 'data': [1, 2]} (安全!)
print(f"深拷贝字典: {deep_copy}") # 输出: 深拷贝字典: {'user': 'Alice', 'data': [1, 2, 3]}
2
3
4
5
6
7
8