Skip to content

Python pickle模块:轻松读写二进制文件

Python 提供了内置的 pickle 模块,用于处理二进制文件的读写操作,特别是用于序列化和反序列化 Python 对象。你可以把 pickle 想象成一个“腌制”工具,它可以把你的 Python 对象(比如列表、字典,甚至是自定义的类)“腌制”成一种特殊的二进制格式,方便存储到硬盘上或者通过网络传输。之后,你可以再用 pickle 把这些“腌制”好的数据“解封”出来,恢复成原来的 Python 对象。

pickle 专为 Python 设计,这意味着它可以存储任何 Python 对象,而不仅仅是基本数据类型。

将 Python 对象转换为可以存储或传输的二进制数据的过程称为序列化 (Serialization),也叫做 pickling(腌制)。相反地,将二进制数据转换回 Python 对象的过程称为反序列化 (Deserialization),也叫做 unpickling(解封)

序列化 (Pickling):将 Python 对象保存到文件

pickle.dump() 函数用于将 Python 对象序列化并写入到文件中。

语法: pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)

让我们分解一下:

  • obj: 你想序列化的 Python 对象。可以是任何东西,比如列表、字典、类的实例等等。
  • file: 一个打开的文件对象,必须以二进制写入模式("wb")或二进制追加模式("ab")打开。 这是 pickle 能够将数据写入的地方。
  • protocol (可选): 指定 pickle 协议的版本。 协议版本越高,序列化效率可能越高,但兼容性可能会受到影响。 默认情况下,使用最高可用的协议。
  • fix_imports (可选): 用于解决 Python 2 和 Python 3 之间模块导入差异的标志。 一般情况下,可以保持默认值 True
  • buffer_callback (可选): 允许你自定义缓冲行为的高级用法,通常不需要使用。

代码示例:

python
import pickle

# 创建一个包含学生信息的列表
student_data = [
    {"name": "Alice", "major": "Computer Science", "grade": 90},
    {"name": "Bob", "major": "Mathematics", "grade": 85},
    {"name": "Charlie", "major": "Physics", "grade": 95}
]

# 将学生数据序列化到名为 "students.pickle" 的文件中
with open("students.pickle", "wb") as file:
    pickle.dump(student_data, file)

print("学生数据已保存到 students.pickle 文件中。")

在这个例子中,我们首先创建了一个包含学生信息的列表 student_data。 然后,我们使用 pickle.dump() 函数将这个列表序列化并写入到名为 students.pickle 的文件中。 注意,我们使用 with open(...) 语句来打开文件, 这样可以确保文件在使用完毕后自动关闭,即使发生错误也是如此。 这是一个良好的编程习惯。 "wb" 确保我们以二进制写入模式打开文件,这对于 pickle 模块是必需的。

反序列化 (Unpickling):从文件加载 Python 对象

pickle.load() 函数用于从文件中读取序列化的数据,并将其反序列化为 Python 对象。

语法: pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)

参数说明:

  • file: 一个打开的文件对象,必须以二进制读取模式("rb")打开。 这是 pickle 从中读取数据的地方。
  • fix_imports (可选): 用于解决 Python 2 和 Python 3 之间模块导入差异的标志。 一般情况下,可以保持默认值 True
  • encoding (可选): 如果你的 pickle 文件包含字符串,可以使用此参数指定编码方式。 默认为 "ASCII"。
  • errors (可选): 指定在解码字符串时如何处理错误。 默认为 "strict"。
  • buffers (可选): 用于支持零拷贝反序列化的高级用法,通常不需要使用。

代码示例:

python
import pickle

# 从 "students.pickle" 文件中加载学生数据
try:
    with open("students.pickle", "rb") as file:
        loaded_data = pickle.load(file)

    # 打印加载的学生数据
    print("从 students.pickle 文件加载的学生数据:")
    for student in loaded_data:
        print(f"姓名: {student['name']}, 专业: {student['major']}, 成绩: {student['grade']}")

except FileNotFoundError:
    print("文件 'students.pickle' 未找到。")
except Exception as e:
    print(f"发生错误: {e}")

在这个例子中,我们使用 pickle.load() 函数从 students.pickle 文件中读取序列化的数据,并将其反序列化为 Python 列表 loaded_data。 然后,我们遍历这个列表,并打印每个学生的姓名、专业和成绩。 "rb" 确保我们以二进制读取模式打开文件。

重要提示:错误处理

在上面的代码中,我们使用了 try...except 块来处理可能发生的错误,例如文件未找到 (FileNotFoundError) 或其他类型的异常。 良好的错误处理可以使你的代码更加健壮。

一个更完整的例子: 学生成绩管理

让我们把学到的东西放在一起,创建一个简单的学生成绩管理程序,它可以将学生成绩保存到文件中,并从文件中加载成绩。

python
import pickle

def add_student(data):
    """添加学生信息"""
    name = input("请输入学生姓名: ")
    major = input("请输入学生专业: ")
    try:
        grade = float(input("请输入学生成绩: "))
    except ValueError:
        print("成绩必须是数字。")
        return

    data.append({"name": name, "major": major, "grade": grade})
    print(f"已添加学生 {name} 的信息。")

def save_data(data, filename="students.pickle"):
    """将学生数据保存到文件"""
    try:
        with open(filename, "wb") as file:
            pickle.dump(data, file)
        print(f"学生数据已保存到 {filename} 文件中。")
    except Exception as e:
        print(f"保存数据时发生错误: {e}")

def load_data(filename="students.pickle"):
    """从文件加载学生数据"""
    try:
        with open(filename, "rb") as file:
            data = pickle.load(file)
        print(f"已从 {filename} 文件加载学生数据。")
        return data
    except FileNotFoundError:
        print(f"文件 {filename} 未找到。")
        return []
    except Exception as e:
        print(f"加载数据时发生错误: {e}")
        return []

def display_data(data):
    """显示学生数据"""
    if not data:
        print("没有学生数据可以显示。")
        return

    print("学生数据:")
    for student in data:
        print(f"  姓名: {student['name']}, 专业: {student['major']}, 成绩: {student['grade']}")

# 主程序
student_data = load_data()  # 尝试加载现有数据,如果文件不存在则创建一个空列表

while True:
    print("\n学生成绩管理系统")
    print("1. 添加学生")
    print("2. 显示学生")
    print("3. 保存数据")
    print("4. 退出")

    choice = input("请选择操作: ")

    if choice == "1":
        add_student(student_data)
    elif choice == "2":
        display_data(student_data)
    elif choice == "3":
        save_data(student_data)
    elif choice == "4":
        print("感谢使用!")
        break
    else:
        print("无效的选择,请重试。")

这个程序允许你添加学生信息、显示学生信息、将数据保存到文件中,以及从文件中加载数据。 这是一个使用 pickle 模块管理数据的完整示例。

pickle 模块的安全性考虑

警告: pickle 模块存在安全风险! 永远不要 unpickle 来自不受信任的来源的数据。

pickle 能够序列化几乎任何 Python 对象,包括代码对象。 这意味着如果有人创建了一个恶意的 pickle 文件,它可能包含在你的机器上执行任意代码的指令。 因此,unpickle 你信任的数据。 通常,这意味着只加载你自己创建的 pickle 文件,或者来自你完全信任的来源的文件。

如果需要处理来自不可信来源的数据, 考虑使用更安全的序列化格式,例如 JSON。

总而言之,pickle 是一个方便的工具,用于在 Python 中序列化和反序列化对象。 但是,请务必注意其安全隐患,并采取适当的预防措施。 通过了解如何使用 pickle 以及它的局限性,你可以有效地利用它来存储和检索 Python 对象。