序列化与反序列化
# Python 序列化与反序列化
在编程中,我们经常需要将程序中的数据结构(如字典、列表、对象等)转换为一种可以被存储或传输的格式,这个过程称为序列化 (Serialization)。反之,将这种格式的数据重新还原为内存中的数据结构,则称为反序列化 (Deserialization)。
为什么需要序列化?
- 数据持久化 (Persistence): 将内存中的对象状态保存到文件或数据库中,以便程序下次运行时可以恢复。例如,保存游戏进度、应用程序配置等。
- 网络传输 (Communication): 在网络上发送数据时,我们不能直接传输内存中的对象。必须先将其序列化为字节流(如 JSON 字符串),然后在接收端再反序列化。
- 跨平台/语言数据交换: 通过一种通用的数据格式(如 JSON 或 XML),让使用不同语言编写的程序能够相互交换数据。
Python 标准库提供了两个核心模块来完成这项工作:json
和 pickle
。
# 1. json
模块:通用数据交换格式
JSON (JavaScript Object Notation) 是一种轻量级的、人类易读的数据交换格式。它独立于任何编程语言,已成为事实上的网络数据传输标准。
特点:
- 通用性强: 几乎所有主流编程语言都有解析 JSON 的库。
- 可读性好: 纯文本格式,易于人类阅读和编写。
- 数据类型有限: 只能表示一部分 Python 内建类型。
Python 与 JSON 类型对应关系:
Python | JSON |
---|---|
dict | object (对象) |
list , tuple | array (数组) |
str | string (字符串) |
int , float | number (数字) |
True / False | true / false |
None | null |
注意:JSON 不支持 Python 的
set
、bytes
或自定义对象等。
# 1.1 序列化:将 Python 对象转为 JSON 字符串
使用 json.dumps()
(dump string) 方法。
import json
# 一个包含多种基本数据类型的 Python 字典
data = {
"name": "小明",
"age": 25,
"is_student": True,
"courses": ["数学", "英语"],
"address": None
}
# 序列化为 JSON 格式的字符串
# indent=4 参数会让输出的字符串格式化,更易读
# ensure_ascii=False 确保中文字符能被正确显示,而不是被转义为 \uXXXX
json_string = json.dumps(data, indent=4, ensure_ascii=False)
print("--- JSON 字符串 ---")
print(json_string)
print(f"\n类型: {type(json_string)}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
输出:
--- JSON 字符串 ---
{
"name": "小明",
"age": 25,
"is_student": true,
"courses": [
"数学",
"英语"
],
"address": null
}
类型: <class 'str'>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 1.2 反序列化:将 JSON 字符串转为 Python 对象
使用 json.loads()
(load string) 方法。
json_string_from_web = """
{
"user_id": 1001,
"username": "alice",
"roles": ["editor", "reviewer"]
}
"""
# 反序列化为 Python 字典
python_dict = json.loads(json_string_from_web)
print("--- Python 字典 ---")
print(python_dict)
print(f"\n类型: {type(python_dict)}")
print(f"用户名: {python_dict['username']}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输出:
--- Python 字典 ---
{'user_id': 1001, 'username': 'alice', 'roles': ['editor', 'reviewer']}
类型: <class 'dict'>
用户名: alice
1
2
3
4
5
2
3
4
5
# 1.3 直接与文件交互:dump()
和 load()
当需要将数据直接写入文件或从文件中读取时,使用 json.dump()
和 json.load()
会更方便。
json.dump(obj, file_pointer)
: 将 Python 对象序列化后写入文件。json.load(file_pointer)
: 从文件中读取 JSON 数据并反序列化。
# 数据
config = {
"theme": "dark",
"font_size": 16,
"plugins": ["spell-check", "auto-save"]
}
# 写入文件 (dump)
# 使用 'w' 模式和 utf-8 编码
with open("config.json", "w", encoding="utf-8") as f:
json.dump(config, f, indent=4, ensure_ascii=False)
print("配置文件 config.json 已保存。")
# 从文件读取 (load)
with open("config.json", "r", encoding="utf-8") as f:
loaded_config = json.load(f)
print("\n从文件中加载的配置:")
print(loaded_config)
print(f"主题: {loaded_config['theme']}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2. pickle
模块:Python 专用序列化协议
pickle
是 Python 特有的序列化模块。它使用二进制协议,几乎可以序列化任何 Python 对象,包括自定义类的实例、函数,甚至复杂的递归数据结构。
特点:
- 功能强大: 支持几乎所有 Python 数据类型。
- Python 专用: 序列化后的数据通常不能被其他语言理解。
- 不保证向后兼容: 不同 Python 版本的
pickle
协议可能不兼容。 - 安全风险:
pickle
在反序列化时可以执行任意代码,因此绝对不要反序列化来自不可信或未经验证来源的数据!
# 2.1 序列化与反序列化
与 json
模块类似,pickle
也提供 dumps/loads
和 dump/load
四个核心函数。
import pickle
import datetime
class User:
def __init__(self, name, last_login):
self.name = name
self.last_login = last_login
def greet(self):
print(f"你好, {self.name}! 你上次登录是在 {self.last_login}.")
# 创建一个复杂的对象,包含自定义类实例和 datetime 对象
user_data = {
'user_obj': User("Alice", datetime.datetime.now()),
'session_id': 12345,
'permissions': {'read', 'write'} # set 类型,json 不支持
}
# 序列化为字节串 (bytes)
pickled_data = pickle.dumps(user_data)
print("--- Pickled 数据 (字节串) ---")
print(pickled_data)
print(f"\n类型: {type(pickled_data)}")
# 反序列化回 Python 对象
unpickled_data = pickle.loads(pickled_data)
print("\n--- 反序列化后的数据 ---")
print(unpickled_data)
# 反序列化后,对象的方法也可以正常调用
restored_user = unpickled_data['user_obj']
restored_user.greet()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 2.2 直接与文件交互
使用 pickle.dump()
和 pickle.load()
进行文件操作时,必须以二进制模式 ('wb'
或 'rb'
) 打开文件。
# 将对象保存到文件
with open("user_data.pkl", "wb") as f: # 'wb' -> write binary
pickle.dump(user_data, f)
print("\n用户数据已保存到 user_data.pkl。")
# 从文件加载对象
with open("user_data.pkl", "rb") as f: # 'rb' -> read binary
loaded_data_from_file = pickle.load(f)
print("\n从 pkl 文件加载的数据:")
loaded_data_from_file['user_obj'].greet()
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 3. json
vs pickle
对比总结
特性 | json | pickle |
---|---|---|
数据格式 | 文本格式,人类可读 | 二进制格式,通常不可读 |
通用性 | 非常高,跨语言、跨平台的标准 | 仅限 Python,不同 Python 版本间可能不兼容 |
支持类型 | 基本数据类型 (dict, list, str, int, bool, None) | 几乎所有 Python 对象 (包括自定义类、函数等) |
性能 | 通常比 pickle 慢,但生成的文本更小 | 通常比 json 快,但生成的二进制文件可能更大 |
安全性 | 安全,只解析数据,不执行代码 | 极不安全,反序列化时可能执行任意代码,切勿用于不可信数据 |
如何选择?
- 当你需要与 Web API、JavaScript前端 或 其他语言的程序 交互时,永远选择
json
。 - 当你需要持久化复杂的 Python 对象(如机器学习模型、游戏状态等),并且数据只在 Python 环境内部使用时,可以使用
pickle
。但一定要确保数据的来源是完全可信的。 - 对于通用的应用程序配置,优先选择
json
或其他人类可读的格式(如 YAML、TOML),因为它们更易于手动检查和修改。
编辑此页 (opens new window)
上次更新: 2025/07/25, 08:02:48