Skip to main content

Python IO 编程

Python 序列化

Python序列化是将数据对象转换为可存储或传输的格式(如字节流或字符串)的过程,反序列化则是将其转换回原始对象。Python提供了多种序列化方法,包括内置的pickle模块和json模块。本章节将详细介绍序列化的概念、Python中的序列化工具,以及如何使用pickle和json实现序列化与反序列化。

1. 什么是序列化?

序列化(Serialization)是将内存中的对象转换为字节流或文本格式,以便存储到文件或通过网络传输。反序列化(Deserialization)则是将这些字节流或文本格式转换回内存中的对象。序列化的主要用途包括:

  • 数据持久化:将对象保存到文件中,以便稍后恢复。
  • 数据传输:在网络中传输对象,例如在分布式系统中。
  • 缓存:将复杂对象保存为文件,减少重复计算。

Python中常用的序列化模块包括pickle(用于二进制序列化)和json(用于文本序列化)。两者各有特点,适用于不同场景。

2. Pickle模块:二进制序列化

pickle是Python内置的模块,支持几乎所有Python对象的序列化,包括自定义类、函数等。它将对象转换为字节流,适合高效存储和传输,但不适合跨语言或安全性要求高的场景。

2.1 Pickle的基本用法

pickle提供以下核心函数:

  • pickle.dump(obj, file):将对象序列化并写入文件。
  • pickle.dumps(obj):将对象序列化为字节对象。
  • pickle.load(file):从文件中反序列化对象。
  • pickle.loads(bytes):从字节对象反序列化。

以下是一个完整的示例,展示如何使用pickle保存和加载数据:

import pickle

# 定义数据
data = {
    "name": "张三",
    "age": 30,
    "scores": [85, 90, 95]
}

# 序列化到文件
with open("data.pkl", "wb") as file:
    pickle.dump(data, file)

# 从文件反序列化
with open("data.pkl", "rb") as file:
    loaded_data = pickle.load(file)

print(loaded_data)

运行结果:

{'name': '张三', 'age': 30, 'scores': [85, 90, 95]}

注意事项

  • 文件操作时,dumpload需要使用二进制模式("wb""rb")。
  • pickle序列化的数据是二进制格式,不适合直接阅读。

2.2 序列化自定义对象

pickle支持序列化自定义类对象。以下是一个序列化自定义类的示例:

import pickle

# 定义一个类
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"Person(name={self.name}, age={self.age})"

# 创建对象
person = Person("李四", 25)

# 序列化到文件
with open("person.pkl", "wb") as file:
    pickle.dump(person, file)

# 从文件反序列化
with open("person.pkl", "rb") as file:
    loaded_person = pickle.load(file)

print(loaded_person)

运行结果:

Person(name=李四, age=25)

2.3 Pickle的安全性问题

pickle在反序列化时可能执行恶意代码,因此不要反序列化不受信任的来源数据。例如,从未知来源的文件或网络加载pickle数据可能导致安全风险。

3. JSON模块:文本序列化

json模块用于将Python对象序列化为JSON格式(字符串),适合跨语言传输和人类可读的场景。JSON支持的数据类型有限,包括:

  • 基本类型:字符串、数字、布尔值、None
  • 容器类型:列表、字典
  • 不支持复杂对象(如自定义类、函数)

3.1 JSON的基本用法

json模块提供以下核心函数:

  • json.dump(obj, file):将对象序列化并写入文件。
  • json.dumps(obj):将对象序列化为JSON字符串。
  • json.load(file):从文件中反序列化JSON数据。
  • json.loads(string):从JSON字符串反序列化。

以下是一个完整示例:

import json

# 定义数据
data = {
    "name": "王五",
    "age": 28,
    "scores": [80, 85, 90],
    "is_student": True,
    "comment": None
}

# 序列化到文件
with open("data.json", "w", encoding="utf-8") as file:
    json.dump(data, file, ensure_ascii=False, indent=4)

# 从文件反序列化
with open("data.json", "r", encoding="utf-8") as file:
    loaded_data = json.load(file)

print(loaded_data)

运行结果:

{'name': '王五', 'age': 28, 'scores': [80, 85, 90], 'is_student': True, 'comment': None}

注意事项

  • ensure_ascii=False确保中文字符不被转义,保持可读性。
  • indent=4使输出的JSON文件格式化,易于阅读。
  • 文件操作需要指定encoding="utf-8"以支持中文。

3.2 序列化自定义对象

JSON不支持直接序列化自定义对象,但可以通过自定义编码器来实现。以下是一个示例:

import json

# 定义一个类
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 自定义JSON编码器
class PersonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Person):
            return {"name": obj.name, "age": obj.age}
        return super().default(obj)

# 创建对象
person = Person("赵六", 22)

# 序列化为JSON字符串
json_str = json.dumps(person, cls=PersonEncoder, ensure_ascii=False)
print(json_str)

# 反序列化(需要手动转换)
loaded_data = json.loads(json_str)
print(loaded_data)

运行结果:

{"name": "赵六", "age": 22}
{'name': '赵六', 'age': 22}

注意:反序列化后,得到的是字典,而不是Person对象。如需恢复为对象,需要额外处理。

4. Pickle与JSON的比较

以下是picklejson的对比,帮助选择合适的序列化工具:

特性 Pickle JSON
格式 二进制 文本(字符串)
可读性 不可读 人类可读
支持的数据类型 几乎所有Python对象(包括自定义类) 基本类型、列表、字典
跨语言支持 仅限Python 跨语言支持
安全性 不安全(可能执行恶意代码) 安全
性能 更快(二进制) 稍慢(文本解析)
使用场景 Python内部数据存储、缓存 跨平台数据交换、配置文件

选择建议

  • 如果数据仅在Python中使用,且需要序列化复杂对象,选择pickle
  • 如果需要跨语言传输或人类可读的格式,选择json

5. 高级话题:序列化的其他工具

除了picklejson,Python生态中还有其他序列化工具,适用于特定场景:

  • shelve:基于pickle,提供类似字典的持久化存储,适合简单键值存储。
  • marshal:Python内部使用的序列化模块,效率高但不适合常规开发。
  • yaml:通过pyyaml库支持,适合复杂配置文件,类似JSON但更灵活。
  • msgpack:高效的二进制序列化库,适合高性能场景。

以下是一个简单的shelve示例:

import shelve

# 存储数据
with shelve.open("mydata") as db:
    db["user"] = {"name": "陈七", "age": 27}

# 读取数据
with shelve.open("mydata") as db:
    user = db["user"]
    print(user)

运行结果:

{'name': '陈七', 'age': 27}

6. 最佳实践与注意事项

  1. 安全性:避免使用pickle反序列化不受信任的数据,优先考虑json
  2. 编码:处理JSON时,始终指定encoding="utf-8"以支持多语言。
  3. 错误处理:序列化和反序列化时,添加异常处理以应对文件错误或格式问题。
  4. 版本兼容性pickle在不同Python版本间可能不兼容,注意版本一致性。
  5. 性能优化:对于大数据量,考虑msgpack或压缩序列化结果。

以下是一个带错误处理的json示例:

import json

try:
    # 尝试加载JSON文件
    with open("data.json", "r", encoding="utf-8") as file:
        data = json.load(file)
    print(data)
except FileNotFoundError:
    print("文件不存在!")
except json.JSONDecodeError:
    print("JSON格式错误!")