要理解 pickle模块的作用,核心是先明确它和你之前用的 json 的核心差异——json 是「跨语言、轻量的文本序列化」,而 pickle 是「Python专属、强功能的二进制序列化」,专门解决 json 搞不定的Python特有序列化场景。
一、先一句话回答:pickle 为什么存在?
pickle 是 Python 内置的 二进制序列化模块,核心目的是:把 Python 中任意复杂的对象(比如自定义类实例、函数、列表嵌套字典、循环引用对象等),完整保存到文件/网络中;后续可以再原样恢复(反序列化),连对象的类型、属性、方法都不会丢。
它的设计初衷就是「为 Python 量身定做」,解决 json 无法处理的 Python 特有序列化需求。
二、pickle 能做什么?(json 做不到的事)
之前你用 json 序列化 Student 对象,其实需要额外处理(比如 default=lambda obj: obj.__dict__),而且 json 有很多限制。而 pickle 完全不用这些“额外操作”,还能处理更复杂的场景:
1. 直接序列化 Python 自定义类实例(无需转换)
用 json 序列化 Student 对象需要手动转字典,但 pickle 可以直接dump/load,无需任何额外配置,反序列化后还是原来的 Student 实例(连类的方法都保留):
import pickle
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def get_grade(self):
return 'A' if self.score >= 90 else 'B'
s = Student('Bob', 20, 88)
# 1. pickle 序列化(直接dump对象,无需转字典)
with open('student.pkl', 'wb') as f:
pickle.dump(s, f) # 直接写对象,二进制模式打开文件('wb')
# 2. pickle 反序列化(直接load回Student实例)
with open('student.pkl', 'rb') as f:
s2 = pickle.load(f)
print(s2.name) # 输出:Bob(属性保留)
print(s2.get_grade()) # 输出:B(方法也保留)
print(isinstance(s2, Student)) # 输出:True(类型完全一致)
2. 序列化 Python 特有的复杂对象
json 只能序列化简单类型(字符串、数字、列表、字典、布尔值、None),但 pickle 能序列化 Python 中几乎所有对象:
- 函数/类(比如把一个自定义函数保存到文件,后续加载后直接调用);
- 复杂数据结构(比如
set、tuple、collections模块的OrderedDict、defaultdict); - 循环引用对象(比如
a = [1]; b = [a]; a.append(b),这种循环引用json会报错,pickle能正常处理); - 内置对象(比如
datetime.datetime时间对象、numpy数组等)。
示例:序列化函数(json 完全做不到)
import pickle
# 定义一个函数
def add(a, b):
return a + b
# 序列化函数到文件
with open('func.pkl', 'wb') as f:
pickle.dump(add, f)
# 反序列化后直接调用函数
with open('func.pkl', 'rb') as f:
add2 = pickle.load(f)
print(add2(3, 5)) # 输出:8(函数功能完全保留)
3. 序列化效率更高、体积更小
pickle 是 二进制格式,而 json 是文本格式:
- 体积:相同数据,
pickle序列化后的文件大小远小于json; - 速度:序列化/反序列化的速度比
json快(尤其是处理大量数据或复杂对象时)。
三、pickle 的核心特点(和 json 对比)
| 特性 | pickle | json |
|---|---|---|
| 适用场景 | Python 内部数据持久化/传输 | 跨语言数据交换(如前后端、Python<->Java) |
| 支持对象类型 | Python 任意对象(类、函数、循环引用等) | 仅简单类型(字符串、数字、列表、字典等) |
| 数据格式 | 二进制(不可读) | 文本(人类可读) |
| 跨语言兼容性 | 无(仅Python可用) | 有(所有语言都支持json) |
| 安全性 | 不安全(加载未知pickle文件可能执行恶意代码) | 安全(文本格式,无执行风险) |
| 使用复杂度 | 低(直接dump/load对象) | 中(复杂对象需手动转换) |
注意:pickle 不安全!如果加载来自未知来源的 .pkl 文件,可能会执行恶意代码(因为它会还原对象的所有属性和方法,包括恶意逻辑),所以只用来处理「自己生成的、可信的数据」。
四、什么时候用 pickle?什么时候用 json?
用 pickle 的场景:
- 你只在 Python 程序之间传输/保存数据(比如 Python 程序的配置、缓存、模型文件,如机器学习训练后的模型
model.pkl); - 需要序列化复杂的 Python 对象(比如自定义类实例、函数、循环引用数据);
- 追求序列化速度和文件体积优化。
用 json 的场景:
- 需要和其他语言(如 Java、JavaScript、Go)交换数据(比如后端给前端返回数据、接口调用);
- 数据需要人类可读(比如配置文件
config.json); - 对数据安全性有要求(避免加载恶意文件)。
五、pickle 基本用法(和 json 很像,但要注意二进制模式)
pickle 的 API 和 json 几乎一致,只是文件操作要用到 二进制模式(wb 写、rb 读):
import pickle
# 1. 序列化对象到文件(dump)
obj = {'name': 'Bob', 'age': 20, 'scores': [88, 92]}
with open('data.pkl', 'wb') as f:
pickle.dump(obj, f) # 二进制写
# 2. 从文件反序列化(load)
with open('data.pkl', 'rb') as f:
obj2 = pickle.load(f) # 二进制读
print(obj2) # {'name': 'Bob', 'age': 20, 'scores': [88, 92]}
# 3. 序列化到内存字节串(dumps)
byte_data = pickle.dumps(obj)
print(type(byte_data)) # <class 'bytes'>(二进制类型)
# 4. 从内存字节串反序列化(loads)
obj3 = pickle.loads(byte_data)
print(obj3 == obj) # True
总结
pickle 的存在,是为了满足 Python 专属的「复杂对象序列化需求」——它放弃了跨语言兼容性和可读性,换来了对 Python 对象的完全支持、更高的效率和更小的体积。
简单说:跨语言/可读用 json,Python 内部复杂对象序列化用 pickle。
