Python中pickle模块的序列化与反序列化(第4节)


在程序运行的过程中,所有的变量都是储存在计算机内存中,但是一旦程序结束运行,这些变量所占用的内存就会被操作系统全部回收,无法长期储存,我们把这些变量转换为可长期储存或可通过网络传输的过程称之为序列化(pickling),我们可以把序列化后的内容储存在磁盘文件或通过网络进行传输。相反,当变量被序列化后,我们把变量内容从序列化的对象重新读取到内存的过程被称为反序列化(deserialization)

简单解释就是,序列化就是将数据结构或者对象转换成二进制字节码(bytes)形式的过程,反序列化就是将序列化过程中生成的二进制字节码(bytes)形式转回成数据结构或对象的过程。

pickle模块

Python标准库中,提供了pickle模块来实现一些基本数据的序列化反序列化,pickle模块的序列化操作可以将程序中运行的数据对象永久存储到文件中,还能通过pickle模块的反序列化操作,将储存在文件中的序列化二进制字节码重新还原成数据对象。

pickle模块的dumps()方法可以把任意对象序列化成二进制bytes数据,然后,就可以把这个二进制bytes数据写入文件。例如:

动手练一练:

import pickle

#创建一个字典对象和一个字符串对象
a = dict(one=4,two=5,three=6)
b = "Pyhint"
print(a) # 输出 {'one': 4, 'two': 5, 'three': 6}
print(b) # 输出 Pyhint

#将这两个对象序列化,c和d仅保存在内存中,可用于网络传输
c = pickle.dumps(a)
d = pickle.dumps(b)
print(c) # 输出二进制字节码"b'\x80\x04\x95\x1f\x00...."
print(d) # 输出二进制字节码"b'\x80\x04\x95\n\x00...."

执行以上代码,输出结果为:

{'one': 4, 'two': 5, 'three': 6}
Pyhint
b'\x80\x04\x95\x1f\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x03one\x94K\x04\x8c\x03two\x94K\x05\x8c\x05three\x94K\x06u.'
b'\x80\x04\x95\n\x00\x00\x00\x00\x00\x00\x00\x8c\x06Pyhint\x94.'

上面的例子中,pickle模块的dumps()方法使用非常简单,可以将Python对象序列化为二进制数据。dumps是“dump string”的缩写,中文意思是“转储字符串”。

pickle模块提供了另一个方法dump(),可以直接把Python对象序列化为文件对象并存入到磁盘文件中,例如:

动手练一练:

import pickle

# 定义一个名为Person的类
class Person:
    # 初始化函数(构造函数)
    def __init__(self, name, age, gender):
        self.name = name   # 将传入的参数赋值给类的属性name
        self.age = age    # 将传入的参数赋值给类的属性age
        self.gender = gender  # 将传入的参数赋值给类的属性gender

# 创建一个Person类的实例
person = Person("张三", 18, "男")

with open("E:\\Pyhint\\Learn-Python\\test\\data.txt", "wb") as p:
    pickle.dump(person, p)

执行以上代码,会在“E:\Pyhint\Learn-Python\test”文件夹下面生成data.txt文件,这个文件包含了代码中person对象的信息。当我们双击打开data.txt文件时,会看到一堆杂乱的二进制数据内容,这些都是pickle保存的对象信息。

上面的例子中,我们已经将序列化的内容保存到磁盘文件中了。相反,当我们要把对象从磁盘文件读到内存中时,可以先把内容读到一个二进制bytes对象中,然后用pickle.loads()方法反序列化出对象,例如:

动手练一练:

import pickle

# 定义一个名为Person的类
class Person:
    # 初始化函数(构造函数)
    def __init__(self, name, age, gender):
        self.name = name   # 将传入的参数赋值给类的属性name
        self.age = age    # 将传入的参数赋值给类的属性age
        self.gender = gender  # 将传入的参数赋值给类的属性gender

a = open("E:\\Pyhint\\Learn-Python\\test\\data.txt", "rb")
b = a.read()
person = pickle.loads(b)
a.close()
print(person.name)  # 输出 张三
print(person.age)  # 输出 18
print(person.gender)  # 输出 男

执行以上代码,输出结果为:

张三
18

loads是“load string”的缩写,中文意思是“加载字符串”。上面的例子中利用pickle.loads()方法,person对象被成功读取出来并加载到变量中,这里需要注意的是,前面不能省略Person类的定义,因为只有定义类之后,才可以创建类的实例对象。

前面我们已经介绍了,可以把Python对象直接序列化为文件对象并存入到磁盘文件中,我们同样可以利用pickle.load()方法直接从文件对象中读取内容,例如:

动手练一练:

import pickle

# 定义一个名为Person的类
class Person:
    # 初始化函数(构造函数)
    def __init__(self, name, age, gender):
        self.name = name   # 将传入的参数赋值给类的属性name
        self.age = age    # 将传入的参数赋值给类的属性age
        self.gender = gender  # 将传入的参数赋值给类的属性gender

with open("E:\\Pyhint\\Learn-Python\\test\\data.txt", "rb") as f:
    person = pickle.load(f)
    print(person.name)  # 输出 张三
    print(person.age)  # 输出 18
    print(person.gender)  # 输出 男

执行以上代码,输出结果为:

张三
18

上面的例子中,执行的结果和我们使用pickle.loads()读取二进制bytes反序列化的效果是一样的。

总之,使用dumps()和loads()会将数据保存在内存变量中,并在内存中序列化和反序列化。如果要将数据序列化后保存到磁盘文件中必须使用dump()方法,相反,从磁盘文件中反序列化还原数据必须使用load()方法,两者末尾只有一个字母“s”的区别,注意不要弄错了。

这里需要注意的是:pickle仅能用于Python程序之间交换数据,且不同的Python版本之间并不兼容,需要和其他程序进行通信时,请使用JSON序列化,它可以在不同编程语言间共享数据。下一节我们将介绍JSON模块的序列化与反序列化。