Pickle模块

发布时间 2023-12-12 14:35:59作者: Magiclala
序列化:把对象转化成二进制字节
反序列化:把二进制字节转化回对象

Pickle模块的常见用法:

Pickle.dunps   把对象(数据)转化成字节
Pickle.loads   把字节转化回对象(数据)
Pickle.dunp   把对象序列化成字节之后写入到文件
Pickle.load   把文件中的字节反序列化成对象

在Python中,序列化和反序列化是将数据结构转换为字节流或其他可存储或传输的格式,以及将字节流或其他格式还原为原始数据结构的过程。

序列化(Serialization)

序列化通常涉及将数据结构转换为字节流或字符串的过程,以便将其保存到文件、数据库或通过网络传输。

使用pickle模块的pickle.dumps()方法进行序列化。

反序列化(Deserialization)

反序列化是将字节流或其他格式还原为原始数据结构的过程。

使用 pickle 模块的 pickle.loads() 方法进行反序列化。

 

案例1:文件存储

 

pickle.dumps() 用于将字典 data 序列化为字节流,并将其写入文件 'data.pkl'

import pickle

data = {'name': 'John', 'age': 30, 'city': 'New York'}

# 序列化
serialized_data = pickle.dumps(data)
with open('data.pkl', 'wb') as file:
    file.write(serialized_data)

pickle.loads() 用于将字节流反序列化为原始的数据结构。

import pickle

# 从文件中读取序列化的数据
with open('data.pkl', 'rb') as file:
    serialized_data = file.read()

# 反序列化
loaded_data = pickle.loads(serialized_data)
print(loaded_data)

案例2:网络传输

pickle.dumps()将列表序列化为字节流,才可以进行网络传输(因为网络底层只认识字节)

import pickle

lst = ["成龙", "赵本山", "范伟"]
bs = pickle.dumps(lst)
print(bs)

#运行结果
b'\x80\x04\x95#\x00\x00\x00\x00\x00\x00\x00]\x94(\x8c\x06\xe6\x88\x90\xe9\xbe\x99\x94\x8c\t\xe8\xb5\xb5\xe6\x9c\xac\xe5\xb1\xb1\x94\x8c\x06\xe8\x8c\x83\xe4\xbc\x9f\x94e.'

pickle.loads()将网络传输过来的字节,重新还原回原始的数据结构。

bs = b'\x80\x04\x95#\x00\x00\x00\x00\x00\x00\x00]\x94(\x8c\x06\xe6\x88\x90\xe9\xbe\x99\x94\x8c\t\xe8\xb5\xb5\xe6\x9c\xac\xe5\xb1\xb1\x94\x8c\x06\xe8\x8c\x83\xe4\xbc\x9f\x94e.'
lst = pickle.loads(bs)
print(lst)

#运行结果
['成龙', '赵本山', '范伟']

需要注意的是,pickle 不是唯一的序列化方法。在实际应用中,你可能还会遇到其他格式,比如 JSON(使用 json 模块)、XML、或者 Protocol Buffers 等,取决于你的需求和使用场景。

反面案例

dic = {"name": "admin", "password": 123}
f = open("data.txt", mode="w", encoding="utf-8")
f.write(dic)

#运行结果
    f.write(dic)
TypeError: write() argument must be str, not dict    #write() 方法中传递的参数必须是字符串(str),而不能是字典(dict)

如果你强制转化成字符串去存储这个字典,str(dic)虽然执行会成功

dic = {"name": "admin", "password": 123}
f = open("data.txt", mode="w", encoding="utf-8")
f.write(str(dic))

但是当你重新读取f.read()文件的时候,读取出来的结果依旧是字符串(str),而不是字典(dict)

f = open("data.txt", mode="r", encoding="utf-8")
s = f.read()
print(s, type(s))

#运行结果,依旧是字符串类型
{'name': 'admin', 'password': 123} <class 'str'>

这样,你就不能像字典一样进行关键字取值、循环等操作。

 

为了解决这个问题,你还得使用eval()进行特殊处理

f = open("data.txt", mode="r", encoding="utf-8")
s = f.read()

d = eval(s)
print(d, type(d))

#运行结果
{'name': 'admin', 'password': 123} <class 'dict'>

但是,使用 eval() 函数来解析字符串是一个潜在的安全风险,因为它可以执行任意的 Python 代码,可能导致代码注入或执行恶意代码的风险。强烈建议避免使用 eval() 来解析未知或不可信来源的数据。

在你的情况下,如果你知道文件中包含的是一个合法的字典表示形式,更安全的方法是使用 json 模块来加载 JSON 数据:

import json

with open("data.txt", mode="r", encoding="utf-8") as f:
    json_str = f.read()

loaded_dict = json.loads(json_str)
print(loaded_dict, type(loaded_dict))

这样,你可以确保加载的数据是一个有效的 JSON 格式,而且不会执行任意的代码。

如果你依然需要处理 Python 字典表示形式而非 JSON,可以使用 ast.literal_eval() 而非 eval(),因为 literal_eval() 更安全,只会解析字面常量,而不会执行任意代码。不过,使用 JSON 通常是更好的选择。