基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Python中的序列化(Pickling)和反序列化(Unpickling)

知识点图片

在 Python 中,序列化(Pickling)反序列化(Unpickling)是将对象转换为字节流以便存储或传输,以及将字节流还原为对象的过程。Python 提供了一个标准模块 pickle 来专门处理这项工作。

以下是详细的指南:


1. 核心概念

  • 序列化 (Pickling):

    • 将 Python 对象层次结构转换为字节流(byte stream)。
    • 用途: 将对象保存到文件、存入数据库、或通过网络传输。
    • 对应函数: dump()dumps()
  • 反序列化 (Unpickling):

    • 将字节流转换回 Python 对象层次结构。
    • 用途: 读取保存的数据,恢复对象的状态。
    • 对应函数: load()loads()

2. 基本用法

首先需要导入模块:

python
import pickle

A. 字符串/字节流操作 (dumpsloads)

当你不需要写入文件,只想把对象变成字节对象(bytes)时使用。

python
import pickle

# 定义一个复杂的对象(字典包含列表)
data = {
    'name': 'Alice',
    'age': 30,
    'scores': [85, 90, 88],
    'is_active': True
}

# 1. 序列化 (Pickling) -> 变为 bytes
binary_data = pickle.dumps(data)
print(f"序列化后的数据类型: {type(binary_data)}")
print(f"序列化后的内容 (部分): {binary_data[:20]}...")

# 2. 反序列化 (Unpickling) -> 变回 对象
restored_data = pickle.loads(binary_data)
print(f"还原后的数据: {restored_data}")
print(f"是否相等: {data == restored_data}")

B. 文件操作 (dumpload)

直接将对象写入文件或从文件读取。注意:必须使用二进制模式 ('wb''rb')。

python
import pickle

data = ['apple', 'banana', 'cherry']

# 1. 序列化到文件
with open('fruit.pkl', 'wb') as f:
    pickle.dump(data, f)
    print("数据已写入 fruit.pkl")

# 2. 从文件反序列化
with open('fruit.pkl', 'rb') as f:
    loaded_data = pickle.load(f)
    print(f"从文件读取的数据: {loaded_data}")

3. 可以被 Pickle 的类型

并不是所有 Python 对象都能被序列化。支持的类型包括:

  • None, True, False
  • 整数、浮点数、复数
  • 字符串、字节 (bytes)、字节数组 (bytearrays)
  • 只包含可 pickle 对象的元组、列表、集合、字典
  • 在模块顶层定义的 函数(通过引用序列化,不存代码逻辑)
  • 在模块顶层定义的
  • 类的实例(如果该类的 __dict____getstate__() 是可序列化的)

通常不能被 Pickle 的对象:

  • 打开的文件句柄(File objects)
  • 网络连接(Sockets)
  • 数据库连接
  • 生成器(Generators)
  • Lambda 函数(因为它们是匿名的,反序列化时很难找到原定义)

4. ⚠️ 极其重要的安全警告

千万不要对不信任的数据进行反序列化(Unpickling)!

pickle 模块不是安全的。构建恶意的 pickle 数据可以在反序列化时执行任意代码。

黑客攻击示例(原理演示):

python
import pickle
import os

class Malicious:
    def __reduce__(self):
        # 当对象被反序列化时,会执行这个命令
        # 这里演示打印一句话,实际上可以是删除文件或窃取数据
        return (os.system, ("echo '你被攻击了!'",))

# 攻击者生成恶意数据
malicious_data = pickle.dumps(Malicious())

# 受害者加载数据
pickle.loads(malicious_data) # 输出: 你被攻击了! (并返回执行结果)

结论: 如果你需要处理来自用户上传或网络的不受信任数据,请使用 JSON


5. Pickle vs JSON

特性 Pickle JSON
通用性 Python 专用 (其他语言无法读取) 通用 (几乎所有语言支持)
数据格式 二进制 (Binary) 文本 (Text/String)
可读性 人类不可读 人类可读
支持类型 支持大多数 Python 类型 (包括自定义类) 仅支持基本类型 (dict, list, str, num, bool)
安全性 不安全 (可执行代码) 安全
速度 通常较快 较慢 (需要解析文本)

选择建议:

  • 如果是 Python 程序内部短期存储状态,且完全信任数据源 \rightarrow Pickle
  • 如果是跨语言交互、存储配置文件、或处理外部数据 \rightarrow JSON

6. 进阶:自定义序列化行为

如果一个对象包含不能被序列化的属性(如打开的文件),你可以通过魔术方法 __getstate____setstate__ 来控制序列化过程。

python
import pickle

class TextReader:
    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename, 'r') # 文件句柄不能被 pickle
        self.content = self.file.read()

    # 序列化时调用:决定存什么
    def __getstate__(self):
        state = self.__dict__.copy()
        # 移除不可序列化的文件句柄
        del state['file']
        return state

    # 反序列化时调用:决定如何恢复
    def __setstate__(self, state):
        self.__dict__.update(state)
        # 重新打开文件
        self.file = open(self.filename, 'r')

# 使用示例(假设 test.txt 存在)
# obj = TextReader('test.txt')
# serialized = pickle.dumps(obj)
# restored = pickle.loads(serialized)

总结

  1. Pickle 是 Python 原生的序列化工具,功能强大但只能在 Python 间使用。
  2. 使用 dump/load 处理文件,dumps/loads 处理内存字节。
  3. 安全第一:永远不要 Unpickle 来源不明的数据。
00:00
00:00