【Python 字典:12 种遍历方式、20+ 个常用操作 与 JSON 序列化,一篇搞定】
Python 字典:12 种遍历方式、20+ 个常用操作 与 JSON 序列化,一篇搞定!
摘要
字典是 Python 中使用频率最高的数据结构之一,但你真的用对了吗?本文从零梳理字典的核心概念,给出 12 种遍历方式(从基础 for 循环到排序遍历、嵌套展开全覆盖),整理 20+ 个常用操作(增删改查、合并、推导式,每个都带可运行代码和输出注释),并深入讲解 JSON 序列化与反序列化的实战技巧(中文不乱码、datetime 等不可序列化对象的处理方案)。文末附完整速查表,适合收藏随时查阅。
不管你是刚学 Python 的新手,还是想系统补全字典知识的老手,这篇 5000+ 字的硬核攻略都能让你一次彻底搞懂 Python 字典!
一、字典基础入门
1.1 什么是字典
字典(dict)是 Python 中存储 键值对(key-value pair) 的数据结构。你可以把它理解为一本真实的字典:键 是你要查的词(word),值 是该词的解释(definition)。通过"键"来快速定位"值",这就是字典的核心用途。
# 一本最简词典
word_book = {
"apple": "苹果",
"banana": "香蕉",
"cat": "猫"
}
print(word_book["apple"]) # 苹果
字典在 Python 3.7+ 中 默认保持插入顺序(3.6 是 CPython 实现细节,3.7 起成为语言规范),这意味着你按什么顺序放进去,遍历时就按什么顺序取出来。
1.2 创建字典的多种方式
除了最常用的 {} 字面量,Python 还提供了多种创建字典的方法,不同场景有不同选择:
# 方式1: 花括号字面量(最常用)
d1 = {"name": "Alice", "age": 25}
# 方式2: dict() 构造函数 + 关键字参数
d2 = dict(name="Bob", age=30)
# 方式3: dict() + 可迭代对象(每个元素是长度为2的序列)
d3 = dict([("name", "Charlie"), ("age", 28)])
# 方式4: 字典推导式
d4 = {x: x ** 2 for x in range(1, 5)}
print(d4) # {1: 1, 2: 4, 3: 9, 4: 16}
# 方式5: dict.fromkeys() 批量创建
d5 = dict.fromkeys(["a", "b", "c"], 0)
print(d5) # {'a': 0, 'b': 0, 'c': 0}
# 方式6: zip() 组合两个列表
keys = ["name", "age", "city"]
values = ["David", 22, "Beijing"]
d6 = dict(zip(keys, values))
print(d6) # {'name': 'David', 'age': 22, 'city': 'Beijing'}
1.3 字典的核心特性
| 特性 | 说明 | 举例 |
|---|---|---|
| 键唯一 | 重复键后面的值覆盖前面的 | {"a": 1, "a": 2} 结果为 {"a": 2} |
| 键必须可哈希 | 键只能是不可变类型(str、int、tuple 等) | {[1, 2]: "a"} 报 TypeError |
| 值任意类型 | 值可以是任何 Python 对象 | {"fn": lambda x: x+1} 合法 |
| 可变 | 可以原地增删改 | d["new_key"] = "val" |
| 动态 | 大小随增删自动调整 | 无需预定义容量 |
| 有序 (3.7+) | 保持键值对的插入顺序 | 遍历顺序 = 添加顺序 |
二、字典的遍历 — 12 种方式全覆盖
遍历字典看似简单,但在不同场景下选择正确的遍历方式,代码可读性和效率会有明显差异。以下按使用频率从高到低整理。
2.1 for key in dict — 直接遍历键
最简洁的写法,for k in d 等价于 for k in d.keys(),但前者更高效(省去方法调用开销)。
score = {"math": 90, "english": 85, "chinese": 92}
for k in score:
print(k, "->", score[k])
# math -> 90
# english -> 85
# chinese -> 92
2.2 .keys() — 显式遍历键
当你需要明确表达"遍历键"的意图,或者要对键做集合运算时使用。
for k in score.keys():
print(k)
# math
# english
# chinese
# .keys() 返回的是动态视图,可以参与集合运算
print(score.keys() & {"math", "art"}) # {'math'}
2.3 .values() — 只遍历值
不关心键是什么,只对值做统计或处理时使用。
for v in score.values():
print(v)
# 90
# 85
# 92
# 常见用法:求和、求平均
total = sum(score.values())
avg = total / len(score)
print(total, avg) # 267 89.0
2.4 .items() — 同时遍历键和值
最常用的遍历方式,一次拿到键和值,避免在循环体内重复取值。
for subject, mark in score.items():
print(f"{subject}: {mark}分")
# math: 90分
# english: 85分
# chinese: 92分
2.5 enumerate() — 带索引遍历
给遍历加上序号,适用于需要显示排名的场景。
for idx, (k, v) in enumerate(score.items(), start=1):
print(f"第{idx}名: {k} {v}分")
# 第1名: math 90分
# 第2名: english 85分
# 第3名: chinese 92分
2.6 sorted() — 按键排序遍历
确保输出顺序完全可控(虽然 3.7+ 字典保持插入顺序,但有时需要按字母或数值排序)。
data = {"banana": 3, "apple": 5, "cherry": 2}
# 按键的字母序
for k in sorted(data):
print(k, data[k])
# apple 5
# banana 3
# cherry 2
2.7 sorted() — 按值排序遍历
按值大小排序输出,统计排行场景必备。
data = {"banana": 3, "apple": 5, "cherry": 2}
# 按值升序
for k, v in sorted(data.items(), key=lambda x: x[1]):
print(k, v)
# cherry 2
# banana 3
# apple 5
# 按值降序
for k, v in sorted(data.items(), key=lambda x: x[1], reverse=True):
print(k, v)
# apple 5
# banana 3
# cherry 2
2.8 reversed() — 逆序遍历
配合 reversed() 从后往前遍历字典项。
d = {"a": 1, "b": 2, "c": 3}
for k in reversed(d):
print(k, d[k])
# c 3
# b 2
# a 1
# 对 items 逆序
for k, v in reversed(d.items()):
print(k, v)
# c 3
# b 2
# a 1
2.9 字典推导式 — 遍历并转换
边遍历边生成新字典,一行代码完成"过滤 + 转换"。
prices = {"apple": 5.5, "banana": 3.2, "orange": 4.8, "grape": 12.0}
# 遍历中过滤:只保留价格 >= 5 的水果
expensive = {k: v for k, v in prices.items() if v >= 5}
print(expensive) # {'apple': 5.5, 'grape': 12.0}
# 遍历中转换:所有商品打八折
discounted = {k: round(v * 0.8, 1) for k, v in prices.items()}
print(discounted) # {'apple': 4.4, 'banana': 2.6, 'orange': 3.8, 'grape': 9.6}
2.10 条件过滤遍历 — 遍历时筛选
不生成新字典,只对符合条件的项做处理。
inventory = {"apple": 10, "banana": 0, "orange": 5, "grape": 0}
# 找出库存为0的商品
for k, v in inventory.items():
if v == 0:
print(f"缺货: {k}")
# 缺货: banana
# 缺货: grape
2.11 嵌套字典遍历 — 多层展开
处理 JSON 数据或配置字典时经常遇到嵌套结构。
students = {
"Alice": {"age": 20, "score": 88},
"Bob": {"age": 21, "score": 92},
"Charlie": {"age": 19, "score": 85}
}
for name, info in students.items():
print(f"{name}: 年龄 {info['age']}, 成绩 {info['score']}")
# Alice: 年龄 20, 成绩 88
# Bob: 年龄 21, 成绩 92
# Charlie: 年龄 19, 成绩 85
2.12 while + 迭代器遍历
不常用,但了解它有助于理解迭代器协议。实际开发中 for 循环是首选。
d = {"x": 10, "y": 20, "z": 30}
it = iter(d.items())
while True:
try:
k, v = next(it)
print(k, v)
except StopIteration:
break
# x 10
# y 20
# z 30
遍历方式速查表
| 遍历方式 | 语法 | 适用场景 |
|---|---|---|
| 直接遍历键 | for k in d: |
只需键,最简洁 |
| 显式遍历键 | for k in d.keys(): |
需要对键做集合运算 |
| 遍历值 | for v in d.values(): |
只关心值,如求和统计 |
| 遍历键值对 | for k, v in d.items(): |
同时需要键和值(最常用) |
| 带索引 | for i, (k,v) in enumerate(d.items()) |
需要显示序号 |
| 按键排序 | for k in sorted(d): |
保证字母/数值顺序 |
| 按值排序 | for k,v in sorted(d.items(), key=...) |
排行、取 Top N |
| 逆序 | for k in reversed(d): |
倒序输出 |
| 推导式 | {k: f(v) for k,v in d.items() if cond} |
过滤+转换,生成新字典 |
| 条件过滤 | if cond: inside for |
只处理符合条件的项 |
| 嵌套展开 | 多层 for + 嵌套索引 |
处理嵌套字典/JSON |
| 迭代器 | while + next() + StopIteration |
理解迭代协议(不推荐日常用) |
三、字典的常见操作 — 20+ 个方法与技巧
3.1 查 — 安全取值
取值是字典最高频的操作,但直接用 d[key] 在 key 不存在时会报 KeyError,因此 get() 往往是更好的选择。
user = {"name": "Alice", "age": 25}
# 方式1: 直接索引(key 不存在报错)
print(user["name"]) # Alice
# 方式2: get() — 安全取值,不存在返回 None
print(user.get("email")) # None
# 方式3: get() + 默认值 — 最佳实践
print(user.get("email", "unknown@example.com")) # unknown@example.com
# 方式4: setdefault() — 取不到就设置并返回
print(user.setdefault("role", "guest")) # guest
print(user) # {'name': 'Alice', 'age': 25, 'role': 'guest'}
| 取值方法 | key 存在 | key 不存在 | 典型用途 |
|---|---|---|---|
d[key] |
返回值 | 抛 KeyError | 确定 key 一定存在时 |
d.get(key) |
返回值 | 返回 None | 不确定 key 是否存在 |
d.get(key, default) |
返回值 | 返回 default | 需要默认值时 |
d.setdefault(key, default) |
返回值 | 设置并返回 default | 初始化缺失的 key |
3.2 增 / 改 — 添加和更新
d = {"a": 1, "b": 2}
# 单个新增/修改
d["c"] = 3
print(d) # {'a': 1, 'b': 2, 'c': 3}
# 批量更新 update()
d.update({"b": 99, "d": 4, "e": 5})
print(d) # {'a': 1, 'b': 99, 'c': 3, 'd': 4, 'e': 5}
# Python 3.9+ 并集运算符 | (返回新字典,不修改原字典)
d2 = {"f": 6, "g": 7}
merged = d | d2
print(merged) # {'a': 1, 'b': 99, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}
print(d) # {'a': 1, 'b': 99, 'c': 3, 'd': 4, 'e': 5} -- 原字典不变
# 原地更新 |=(Python 3.9+)
d |= d2
print(d) # {'a': 1, 'b': 99, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}
# ** 解包合并(兼容老版本,返回新字典)
d3 = {"x": 10, "y": 20}
combined = {**d, **d3}
print(combined) # {'a': 1, 'b': 99, ..., 'x': 10, 'y': 20}
3.3 删 — 安全删除
d = {"a": 1, "b": 2, "c": 3, "d": 4}
# pop(key) — 删除并返回值,key 不存在报错
val = d.pop("c")
print(val, d) # 3 {'a': 1, 'b': 2, 'd': 4}
# pop(key, default) — 安全删除,不存在返回默认值
val = d.pop("z", "not found")
print(val) # not found
# popitem() — 删除并返回最后一个键值对(LIFO)
last = d.popitem()
print(last, d) # ('d', 4) {'a': 1, 'b': 2}
# del — 直接删除(key 不存在报 KeyError)
del d["b"]
print(d) # {'a': 1}
# clear() — 清空所有元素
d.clear()
print(d) # {}
3.4 判断 — 存在性与长度
d = {"name": "Alice", "age": 25}
# in 运算符 — 判断键是否存在(O(1) 时间复杂度)
print("name" in d) # True
print("email" in d) # False
print("email" not in d) # True
# len() — 键值对数量
print(len(d)) # 2
# 判空
if not d:
print("字典为空")
else:
print(f"字典有 {len(d)} 项")
# 字典有 2 项
3.5 视图对象 — keys / values / items
这三个方法返回的都是 动态视图(view object),会随原字典变化而变化。
d = {"a": 1, "b": 2}
keys_view = d.keys()
vals_view = d.values()
items_view = d.items()
print(keys_view) # dict_keys(['a', 'b'])
print(vals_view) # dict_values([1, 2])
print(items_view) # dict_items([('a', 1), ('b', 2)])
# 动态性:修改原字典后,视图自动反映变化
d["c"] = 3
print(keys_view) # dict_keys(['a', 'b', 'c'])
print(vals_view) # dict_values([1, 2, 3])
3.6 复制 — 浅拷贝与深拷贝
import copy
original = {"a": [1, 2, 3], "b": {"nested": True}}
# 浅拷贝:顶层独立,嵌套对象共享引用
shallow = original.copy() # 等价于 dict(original) 或 {**original}
shallow["a"].append(4)
print(original["a"]) # [1, 2, 3, 4] — 原字典也被影响!
# 深拷贝:完全独立,递归复制所有嵌套对象
deep = copy.deepcopy(original)
deep["a"].append(5)
print(original["a"]) # [1, 2, 3, 4] — 原字典不受影响
3.7 fromkeys() — 批量初始化
# 用同一个默认值初始化所有键
keys = ["name", "age", "city"]
d = dict.fromkeys(keys, "N/A")
print(d) # {'name': 'N/A', 'age': 'N/A', 'city': 'N/A'}
# 陷阱:默认值是可变对象时,所有键共享同一个对象!
d_bad = dict.fromkeys(["a", "b", "c"], [])
d_bad["a"].append(1)
print(d_bad) # {'a': [1], 'b': [1], 'c': [1]} — 全部被改了!
# 正确做法:用字典推导式给每个键创建独立对象
d_good = {k: [] for k in ["a", "b", "c"]}
d_good["a"].append(1)
print(d_good) # {'a': [1], 'b': [], 'c': []}
3.8 字典推导式 — 一行搞定过滤和转换
# 过滤:只保留成绩 >= 80 的高分选手
scores = {"Alice": 90, "Bob": 55, "Charlie": 85, "David": 72}
passed = {k: v for k, v in scores.items() if v >= 80}
print(passed) # {'Alice': 90, 'Charlie': 85}
# 转换:成绩转为等级
grades = {k: "优" if v >= 90 else "良" if v >= 70 else "中" if v >= 60 else "差"
for k, v in scores.items()}
print(grades) # {'Alice': '优', 'Bob': '差', 'Charlie': '良', 'David': '中'}
# 键值互换
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}
四、JSON 序列化与反序列化
字典和 JSON 是天生的搭档——Python 的 dict 与 JSON 的 object 高度对应。但序列化过程中有不少坑,这一章帮你全部避开。
4.1 核心概念
| 操作 | 方向 | 函数 | 输入 | 输出 |
|---|---|---|---|---|
| 序列化 | Python → JSON 字符串 | json.dumps() |
dict/list/str/… | str |
| 反序列化 | JSON 字符串 → Python | json.loads() |
str | dict/list/… |
| 写文件 | Python → JSON 文件 | json.dump() |
dict + file | 写入文件 |
| 读文件 | JSON 文件 → Python | json.load() |
file | dict/list/… |
4.2 dumps() / loads() — 内存中的序列化
import json
# 序列化:dict → JSON 字符串
user = {"name": "Alice", "age": 25, "active": True, "tags": ["python", "web"]}
json_str = json.dumps(user)
print(json_str)
# {"name": "Alice", "age": 25, "active": true, "tags": ["python", "web"]}
print(type(json_str)) # <class 'str'>
# 反序列化:JSON 字符串 → dict
parsed = json.loads(json_str)
print(parsed) # {'name': 'Alice', 'age': 25, 'active': True, 'tags': ['python', 'web']}
print(type(parsed)) # <class 'dict'>
print(parsed["name"]) # Alice
4.3 中文不乱码 — ensure_ascii=False
默认情况下 json.dumps() 会把非 ASCII 字符转义为 \uXXXX 格式。中文场景必须加上 ensure_ascii=False。
data = {"title": "Python 字典入门教程", "author": "张三"}
# 默认行为:中文被转义,完全不可读
print(json.dumps(data))
# {"title": "Python 字典入门教程", "author": "张三"}
# 加上 ensure_ascii=False:中文原样保留
print(json.dumps(data, ensure_ascii=False))
# {"title": "Python 字典入门教程", "author": "张三"}
4.4 排版美化 — indent 与 sort_keys
调试和日志场景下,紧凑的 JSON 难以阅读,使用 indent 和 sort_keys 可以大幅提升可读性。
config = {
"server": {"host": "0.0.0.0", "port": 8080},
"database": {"engine": "postgresql", "pool_size": 10},
"debug": False
}
pretty = json.dumps(config, indent=2, ensure_ascii=False, sort_keys=True)
print(pretty)
# {
# "database": {
# "engine": "postgresql",
# "pool_size": 10
# },
# "debug": false,
# "server": {
# "host": "0.0.0.0",
# "port": 8080
# }
# }
4.5 dump() / load() — 读写 JSON 文件
# 写入 JSON 文件
config = {"version": "1.0", "settings": {"theme": "dark", "lang": "zh"}}
with open("config.json", "w", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=2)
# 文件内容:
# {
# "version": "1.0",
# "settings": {
# "theme": "dark",
# "lang": "zh"
# }
# }
# 读取 JSON 文件
with open("config.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded["settings"]["theme"]) # dark
4.6 处理不可序列化的对象 — default 参数
datetime、Decimal、自定义类等对象无法直接序列化,json.dumps() 会抛 TypeError。解决方案是自定义 default 函数。
from datetime import datetime, date
from decimal import Decimal
data = {
"user": "Alice",
"created_at": datetime.now(),
"birthday": date(2000, 1, 15),
"balance": Decimal("99.99")
}
# 方式1:自定义 default 函数
def json_default(obj):
if isinstance(obj, datetime):
return obj.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(obj, date):
return obj.strftime("%Y-%m-%d")
if isinstance(obj, Decimal):
return float(obj)
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
result = json.dumps(data, default=json_default, ensure_ascii=False, indent=2)
print(result)
# {
# "user": "Alice",
# "created_at": "2026-07-20 10:30:00",
# "birthday": "2000-01-15",
# "balance": 99.99
# }
# 方式2:继承 JSONEncoder(适合复用场景)
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(obj, date):
return obj.strftime("%Y-%m-%d")
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj)
result2 = json.dumps(data, cls=CustomEncoder, ensure_ascii=False)
4.7 自定义反序列化 — object_hook
有时需要把 JSON 还原为特定的 Python 对象,用 object_hook 可以拦截每一个被解析的 dict。
json_str = '''
{
"user": "Bob",
"created_at": "2026-07-20 14:30:00",
"balance": 128.50
}
'''
from datetime import datetime
def revive_dates(dct):
"""如果发现 created_at 字段,自动转回 datetime 对象"""
result = {}
for k, v in dct.items():
if k.endswith("_at") and isinstance(v, str):
try:
result[k] = datetime.strptime(v, "%Y-%m-%d %H:%M:%S")
except ValueError:
result[k] = v
else:
result[k] = v
return result
parsed = json.loads(json_str, object_hook=revive_dates)
print(parsed)
# {'user': 'Bob', 'created_at': datetime.datetime(2026, 7, 20, 14, 30), 'balance': 128.5}
print(type(parsed["created_at"])) # <class 'datetime.datetime'>
4.8 Python dict 与 JSON 类型对照
| Python 类型 | JSON 类型 | 说明 |
|---|---|---|
dict |
object |
键必须是字符串 |
list, tuple |
array |
tuple 序列化后变 array,反序列化回 list |
str |
string |
— |
int, float |
number |
JSON 不区分整数和浮点数 |
True, False |
true, false |
注意大小写 |
None |
null |
— |
datetime, Decimal 等 |
不支持 | 需要自定义 default 函数 |
4.9 常见序列化陷阱与解决方案
| 陷阱 | 现象 | 解决方案 |
|---|---|---|
| 中文乱码 | 中文 |
加 ensure_ascii=False |
| datetime 报错 | TypeError: Object is not JSON serializable |
用 default 参数转字符串 |
| tuple 变 list | 反序列化后 tuple 丢失 | 用 object_hook 手动恢复 |
| 大文件内存溢出 | 加载大 JSON 文件卡顿 | 用 ijson 等流式解析库 |
| 浮点数精度丢失 | 1.1 + 2.2 不是精确的 3.3 |
用 Decimal + 自定义序列化 |
五、总结速查表
5.1 字典方法速查
| 方法 | 说明 | 返回值 |
|---|---|---|
d[key] |
访问元素 | 值(不存在报错) |
d.get(key, default) |
安全访问 | 值或 default |
d.keys() |
获取所有键 | dict_keys 视图 |
d.values() |
获取所有值 | dict_values 视图 |
d.items() |
获取所有键值对 | dict_items 视图 |
d.update(other) |
批量更新 | None(原地修改) |
d[key] = val |
添加/修改 | — |
d.pop(key, default) |
删除并返回值 | 值或 default |
d.popitem() |
删除最后一项 | (key, value) 元组 |
del d[key] |
删除元素 | —(不存在报错) |
d.clear() |
清空所有元素 | None |
d.copy() |
浅拷贝 | 新 dict |
d.setdefault(key, default) |
取值,不存在则设置 | 值 |
dict.fromkeys(keys, val) |
批量创建(类方法) | 新 dict |
key in d |
键是否存在 | bool |
len(d) |
元素数量 | int |
d1 | d2 (3.9+) |
合并(返回新字典) | 新 dict |
d1 |= d2 (3.9+) |
合并(原地更新) | — |
{**d1, **d2} |
解包合并 | 新 dict |
{k: v for ...} |
字典推导式 | 新 dict |
5.2 json 模块速查
| 函数 | 方向 | 适用场景 |
|---|---|---|
json.dumps(obj) |
Python → str | 内存中序列化 |
json.loads(s) |
str → Python | 内存中反序列化 |
json.dump(obj, f) |
Python → 文件 | 写入 JSON 文件 |
json.load(f) |
文件 → Python | 读取 JSON 文件 |
dumps(..., ensure_ascii=False) |
— | 保留中文 |
dumps(..., indent=2) |
— | 美化输出 |
dumps(..., default=fn) |
— | 自定义序列化 |
loads(..., object_hook=fn) |
— | 自定义反序列化 |
本文从字典基础、12 种遍历方式、20+ 个常用操作,到 JSON 序列化与反序列化的实战技巧,已经全部覆盖。如果觉得有用,欢迎收藏、点赞,有任何问题或者你有更好用的字典技巧,也欢迎在评论区分享交流!
(本文使用 Python 3.10 环境,所有代码均可直接运行。)
更多推荐



所有评论(0)