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 难以阅读,使用 indentsort_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 参数

datetimeDecimal、自定义类等对象无法直接序列化,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 环境,所有代码均可直接运行。)

更多推荐