在 Python 3 中,字典(Dictionary) 是最强大、最常用的数据结构之一。它通过键值对(Key-Value pairs) 来存储数据,底层基于哈希表(Hash Table) 实现,这使得它的查找、插入和删除操作平均时间复杂度都达到了惊人的 O(1)。
💡 重要特性:从 Python 3.7 开始,字典严格保证保持元素的插入顺序。这是现代 Python 的一个重大改进。
下面我将从基础操作、高级技巧、现代语法到常见陷阱,为你全面解析 Python 3 字典。
一、 创建与初始化字典
有多种方式可以创建字典,适用于不同的场景。
# 1. 字面量创建 (最常用、最直观)
user = {"name": "Alice", "age": 25, "city": "Beijing"}
# 2. 使用 dict() 构造函数
# 方式 A: 传入关键字参数 (键必须是合法的标识符,即字符串且不能以数字开头)
config = dict(host="localhost", port=8080, debug=True)
# 方式 B: 传入包含键值对的元组列表
items = dict([("name", "Bob"), ("age", 30)])
# 3. 使用 fromkeys() 快速创建具有相同默认值的字典
keys = ["a", "b", "c"]
default_dict = dict.fromkeys(keys, 0)
print(default_dict) # 输出: {'a': 0, 'b': 0, 'c': 0}二、 核心操作:增、删、改、查
my_dict = {"apple": 3, "banana": 5}
# 1. 查 (访问)
print(my_dict["apple"]) # 输出: 3
# print(my_dict["orange"]) # ❌ 会抛出 KeyError: 'orange'
# ✅ 安全访问:使用 .get(),找不到时返回 None 或指定的默认值
print(my_dict.get("orange", 0)) # 输出: 0 (强烈推荐这种做法)
# 2. 改 (修改)
my_dict["apple"] = 10 # 键存在,则更新值
# 3. 增 (添加)
my_dict["orange"] = 8 # 键不存在,则添加新键值对
# 4. 删 (删除)
del my_dict["banana"] # 删除指定键,不存在会报 KeyError
value = my_dict.pop("apple") # 删除并返回该键的值,可带默认值防止报错
last_item = my_dict.popitem() # 删除并返回**最后插入**的键值对 (Python 3.7+)
my_dict.clear() # 清空字典三、 遍历字典的 3 种标准姿势
在循环中处理字典时,Python 提供了极其优雅的方法。
scores = {"Alice": 95, "Bob": 88, "Charlie": 92}
# 1. 遍历键 (默认行为,等同于 scores.keys())
for key in scores:
print(key)
# 2. 遍历值
for value in scores.values():
print(value)
# 3. 遍历键值对 (最常用,利用元组解包)
for name, score in scores.items():
print(f"{name} 的分数是 {score}")四、 高级技巧与现代语法 (Python 3.5+)
1. 字典合并与更新
.update():就地更新字典(修改原字典)。python dict1 = {"a": 1, "b": 2} dict2 = {"b": 3, "c": 4} dict1.update(dict2) print(dict1) # 输出: {'a': 1, 'b': 3, 'c': 4} (b 的值被覆盖)|和|=运算符 (Python 3.9+ 新增):更优雅的合并方式。dict1 = {"a": 1, "b": 2} dict2 = {"b": 3, "c": 4} # | 创建新字典,不修改原字典 merged = dict1 | dict2 # {'a': 1, 'b': 3, 'c': 4} # |= 就地更新 dict1 dict1 |= dict2
2. 字典解包 (**)
在函数传参或合并字典时非常有用。
defaults = {"host": "localhost", "port": 80}
overrides = {"port": 8080, "debug": True}
# 合并字典 (Python 3.5+)
config = {**defaults, **overrides}
print(config) # 输出: {'host': 'localhost', 'port': 8080, 'debug': True}3. setdefault():优雅的“如果不存在则设置”
当你需要初始化字典中的列表或集合时,这个方法是神器。
# 场景:按类别分组数据
data = [("fruit", "apple"), ("fruit", "banana"), ("color", "red")]
groups = {}
for category, item in data:
# 如果 category 不存在,先设为空列表 [],然后 append
groups.setdefault(category, []).append(item)
print(groups)
# 输出: {'fruit': ['apple', 'banana'], 'color': ['red']}(注:对于复杂的分组场景,前文提到的 collections.defaultdict 是更好的选择)
4. 字典推导式 (Dictionary Comprehension)
与列表推导式类似,用于快速生成或转换字典。
numbers = [1, 2, 3, 4]
# 生成 {数字: 数字的平方} 的字典
squares_dict = {x: x**2 for x in numbers if x % 2 == 0}
print(squares_dict) # 输出: {2: 4, 4: 16}
# 翻转字典的键和值 (前提是值是可哈希的且唯一)
original = {"a": 1, "b": 2}
flipped = {v: k for k, v in original.items()}
print(flipped) # 输出: {1: 'a', 2: 'b'}五、 ⚠️ 常见陷阱与最佳实践
陷阱 1:键必须是“可哈希的” (Hashable)
字典的键必须是不可变类型(如字符串、数字、元组)。不能使用列表、字典或集合作为键。
# ❌ TypeError: unhashable type: 'list'
bad_dict = {[1, 2]: "value"}
# ✅ 正确做法:使用元组作为键
good_dict = {(1, 2): "value"}(值可以是任何类型,包括列表或字典)
陷阱 2:在遍历字典时修改它的大小
在 for 循环遍历字典时,如果添加或删除键,会导致 RuntimeError: dictionary changed size during iteration。
my_dict = {"a": 1, "b": 2, "c": 3}
# ❌ 错误:遍历时删除
# for k in my_dict:
# if k == "b":
# del my_dict[k]
# ✅ 正确做法 1:遍历字典键的副本 (list)
for k in list(my_dict.keys()):
if k == "b":
del my_dict[k]
# ✅ 正确做法 2:使用字典推导式生成新字典
my_dict = {k: v for k, v in my_dict.items() if k != "b"}陷阱 3:误以为字典是“按字母顺序”排序的
Python 3.7+ 保证的是插入顺序,而不是按键的字母或数字大小排序。如果需要排序,必须显式使用 sorted()。
d = {"z": 1, "a": 2, "m": 3}
# 按键排序
sorted_by_key = dict(sorted(d.items()))
print(sorted_by_key) # 输出: {'a': 2, 'm': 3, 'z': 1}六、 总结:何时使用字典?
| 你的需求 | 推荐数据结构 |
|---|---|
| 需要通过名称/标识符快速查找对应的数据 | dict (例如:通过用户 ID 查用户信息) |
| 需要记录配置项、参数或具有明确属性的实体 | dict (或 Python 3.7+ 的 @dataclass) |
| 需要统计元素出现次数 | collections.Counter (字典的子类) |
| 只需要存储唯一值,不需要关联数据 | set (集合) |
字典是 Python 的灵魂数据结构。掌握了 .get() 防错、.items() 遍历、推导式以及 Python 3.9 的 | 合并运算符,你的 Python 代码就会显得非常专业和现代。
你目前是否有处理复杂 JSON 数据、或者需要嵌套字典的具体场景?我可以为你演示如何优雅地解析和操作它们!