序列化

Python 序列化与反序列化指南

1. 序列化概述

在程序运行过程中,变量存储在内存中。当程序结束时,内存会被操作系统回收。为了持久化数据,我们需要将内存中的对象转换为可存储或传输的格式,这个过程称为序列化(在Python中称为pickling)。反之,将序列化后的数据重新转换为内存中的对象称为反序列化(unpickling)。

2. Python pickle 模块

Python 提供了 pickle 模块来实现序列化。

2.1 基本用法

import pickle

# 创建一个字典对象
data = {'name': 'Bob', 'age': 20, 'score': 88}

# 序列化为 bytes
serialized_data = pickle.dumps(data)
print(serialized_data)  # 输出二进制数据

# 将序列化数据写入文件
with open('data.pkl', 'wb') as f:
    pickle.dump(data, f)

# 从文件反序列化
with open('data.pkl', 'rb') as f:
    loaded_data = pickle.load(f)
    print(loaded_data)  # 输出: {'name': 'Bob', 'age': 20, 'score': 88}

2.2 pickle 的局限性

  • Python 专属:只能用于 Python 程序之间交换数据
  • 版本兼容性:不同 Python 版本可能不兼容
  • 安全性问题:不要反序列化不受信任的来源的数据,可能执行恶意代码

3. JSON 序列化

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,具有跨语言、易读的特点。

3.1 基本数据类型对应关系

JSON 类型Python 类型
{}dict
[]list
"string"str
1234.56int/float
true/falseTrue/False
nullNone

3.2 基本用法

import json

# Python 对象转 JSON 字符串
data = {'name': 'Bob', 'age': 20, 'score': 88}
json_str = json.dumps(data)
print(json_str)  # 输出: {"name": "Bob", "age": 20, "score": 88}

# JSON 字符串转 Python 对象
loaded_data = json.loads(json_str)
print(loaded_data)  # 输出: {'name': 'Bob', 'age': 20, 'score': 88}

# 文件操作
with open('data.json', 'w') as f:
    json.dump(data, f)

with open('data.json', 'r') as f:
    loaded_from_file = json.load(f)
    print(loaded_from_file)

3.3 处理中文字符

data = {'name': '小明', 'age': 20}

# ensure_ascii=True (默认): 中文会被转义
print(json.dumps(data))  # 输出: {"name": "\\u5c0f\\u660e", "age": 20}

# ensure_ascii=False: 保留中文
print(json.dumps(data, ensure_ascii=False))  # 输出: {"name": "小明", "age": 20}

4. 序列化自定义对象

4.1 简单方法:使用 __dict__

class Student:
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score

s = Student('Bob', 20, 88)

# 序列化
json_str = json.dumps(s, default=lambda obj: obj.__dict__)
print(json_str)  # 输出: {"name": "Bob", "age": 20, "score": 88}

# 反序列化
def dict_to_student(d):
    return Student(d['name'], d['age'], d['score'])

loaded_s = json.loads(json_str, object_hook=dict_to_student)
print(loaded_s.name, loaded_s.age, loaded_s.score)  # 输出: Bob 20 88

4.2 更灵活的方法:实现序列化方法

class Student:
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score
    
    def to_dict(self):
        return {
            'name': self.name,
            'age': self.age,
            'score': self.score,
            '__class__': 'Student'
        }
    
    @classmethod
    def from_dict(cls, d):
        if d.get('__class__') == 'Student':
            return cls(d['name'], d['age'], d['score'])
        return d

s = Student('Bob', 20, 88)

# 序列化
json_str = json.dumps(s, default=lambda obj: obj.to_dict())
print(json_str)

# 反序列化
loaded_s = json.loads(json_str, object_hook=Student.from_dict)
print(isinstance(loaded_s, Student))  # 输出: True

5. 安全注意事项

  1. 不要反序列化不受信任的 pickle 数据 - 可能执行任意代码
  2. 验证 JSON 输入 - 确保数据结构符合预期
  3. 处理异常 - 反序列化可能失败,需要捕获异常
try:
    data = json.loads(malformed_json)
except json.JSONDecodeError as e:
    print(f"Invalid JSON: {e}")

6. 性能优化

对于大型数据结构:

  1. 使用 ujsonorjson 替代标准 json 模块提高速度
  2. 考虑使用二进制格式如 MessagePackProtocol Buffers 处理大数据
# 使用 orjson 示例 (需要安装: pip install orjson)
import orjson

data = {'name': 'Bob', 'age': 20, 'score': 88}
json_bytes = orjson.dumps(data)  # 返回 bytes 而不是 str
print(json_bytes)

7. 总结

  • Python 内部使用pickle 模块,高效但不安全且不跨语言
  • 跨语言数据交换json 模块,安全、跨语言但性能较低
  • 自定义对象:实现 to_dict()from_dict() 方法
  • 中文处理:使用 ensure_ascii=False
  • 安全:始终验证输入,处理异常
  • 性能:对于大型数据考虑替代方案

选择序列化方法时,应根据具体需求权衡安全性、性能和兼容性。