[python] pickle 模块 -- 实现数据简单存储

今天要给大家介绍的,是 pickle 模块。通过词典我们可以知道, pickle 这个词的意思是“泡菜”。听了这么个名字,大家是不是更加疑惑了?难道我们要把程序放进一个缸里,让他自己发酵?哈哈,当然不是这样。

pickle 模块的产生是为了解决文件读取中的一大问题:如何方便地保存和读取数据。许多时候我们写入一个文件,只是为了将数据保存,为了方便下一个程序读取它。而一般存储的文件都是以文本形式,这样就容易导致互相的不兼容。所以产生了两种常用的标准存储形式: CSVJSON 。下面先简单介绍一下它们:

1. CSV 的原理

CSV 可以较为方便地存储一个一维或者二维的列表。它用逗号分隔不同的数据,用换行来表示下一组数据。以下是 Wiki-CSV 上的介绍:

In computing, a comma-separated values (CSV) file is a delimited text file that uses a comma to separate values. A CSV file stores tabular data (numbers and text) in plain text. Each line of the file is a data record. Each record consists of one or more fields, separated by commas. The use of the comma as a field separator is the source of the name for this file format.

CSV 使用起来较为简单,python 中也有官方的 CSV 模块,我们用代码也可以实现。比如有下面这样一个 CSV 格式的字符串。

'12,24,46'

在 python 中,我们只需要用到字符串中的 split 方法,便可以实现将数据恢复为列表:

ls = '12,24,46'.split(',')

但是要将列表转化为 CSV 格式就稍微复杂些了。我们常用 ‘,’.join(ls) 这样的方法将列表转化为字符串,但是 join 方法只支持所有元素全部为字符串的列表,如果列表中有数字等类型就会报错。而且二维列表就需要循环来输出每一行。这个实现方法不难,但是代码有些冗长,所以不贴上来了。如果各位有兴趣可以尝试自己写一写。

但是 CSV 的存储还有一些问题,比如它不能存储类似于对象/结构体这样的复杂的类型。而且如果数据中含有逗号这个字符,那么 CSV 便无法正确地将数据分隔。遇到这样的问题, JSON 就是另一种解决方案。

2. JSON 与 pickle 的区别

JSON 是一种较为通用的数据存储结构,它可以在多种语言之间相互使用。 JSON 的语法较为复杂,这里就不再详细介绍。JSON 能够实现任意类型的数据存储,这使得它比 CSV 有更广的适用性。 python 也带有 JSON 的官方模块,也可以比较方便地实现读取存储功能,但是对于不多的数据, JSON 便有些“大材小用”了(JSON 相较于 pickle 还有一点优点就是它可以方便地可视化,因为 pickle 保存后的文件为二进制格式,无法直接用文本编辑器直接打开;但这样相对来说 pickle 的文件较为安全,它被直接打开或是被中途修改的可能性就更小)

然而假如你仅仅只需要在 python 中存储几个数据。使用上面两种方法便也有些过于复杂。如果你不需要用到通用的数据存储, pickle 就是一个最好的选择。

3. pickle 的常用函数

pickle 模块只需要用简单的 dump(obj, file) 函数就可以实现写入文件,而使用 load(file) 函数便可读取数据。(其中 obj 是待写入的对象,file 是文件对象。 dump()load() 函数都可以多次使用,会在原来的基础上继续读写,这样可以存储多个数据。)

比如说我们下面创建两个实例 example1.pyexample2.py 并且尝试在它们之间传输一个列表和一个数据。为了增大难度,我们将列表变为三维,混合了数据类型,并且将其中一部分改为元组。

#example1.py
import pickle
f = open("abc.pickle", 'wb+')
ls = [[(23, 29), (22, 79)], 'hello', '35']
num = 3.1415926
pickle.dump(ls, f)
pickle.dump(num, f)
#example2.py
import pickle
f = open("abc.pickle", 'rb')
newls = pickle.load(f)
newnum = pickle.load(f)
print(newls)
print(newnum)

我们先运行 example1.py ,这个时候便会产生一个叫做 abc.pickle 的文件。文件名和后缀怎么取名都可以。我们先尝试用文本编辑器打开这个文件,便会显示以下的内容。

]q(]q(KKqKKOqeXhelloqX35qe.G@	!MJ.

之前说过, pickle 输出的数据是**二进制(流)**的格式,它不能够被直接以文本形式打开。所以在刚刚两个实例中,都要使用 wbrb 写入、读取文件。

现在我们打开 example2.py 并运行,可以看到控制台成功输出了 example1.py 程序中写入的结果,甚至连列表和元组的类型都没有改变:

[[(23, 29), (22, 79)], 'hello', '35']
3.1415926

pickle 模块的神奇之处在于它可以让你免去文件“I/O”中的转换环节。让你能够更快地完成数据的存储功能。当然, pickle.load() 函数的预设是 ASCII 编码,所以假如读取后中文出现了乱码(应该会直接报错),这时我们修改 encoding 参数就可以了。

pickle.load(f, encoding = 'gb2312')

4. dumps() 与 loads() - 不一样的功能

在自动补全时,我发现 pickle 还有 dumps()loads() 这两个函数。一开始我以为它做的是将多个数据存入一个文件中(这个用多个 dump() 函数就可以实现),然而还是发现 Too Young 了。查阅了官方文档之后,发现这两个的作用与 dump() load() 不同。 dump() 是直接将生成好的二进制(流)保存到文件中,而 dumps() 是直接返回一个二进制(流)。(没有写入文件,而是可以赋给其他变量)

平时,我们可能并不会去使用这两个函数。不过其实在之后的使用中,它们可以用于在数据模块中存储 python 数据类型。例如 Redis 的键值并不能支持全部的 python 类型,但是如果用 dumps() 将一个二进制流传入,读取的时候再用loads() 完整取出,就可以简单地实现多种数据格式的存储。如果感兴趣的话可以看一下我的另外一个文档:[python] redis 模块

结语与其他文档

学习了 pickle 的功能,我们可以猜想它的取名可能影射的是作者想要实现将数据“完整地放入”并“完整地取出”的功能,不过这也有可能是作者的一种情怀。看完 pickle 这个模块之后,你是不是对 python 存储数据有了更深的认识呢?当然如果大家想要更加深入地了解这一个模块,在 12.1. pickle — Python object serialization 中也有安全提示、相关模块、支持类型等更详细的文档。

转载于:https://my.oschina.net/u/3729927/blog/1929861

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Pythonpickle模块是用来实现序列化的,即将Python中的对象转换成字节流,方便存储和传输。pickle模块支持多种协议,其中协议0是最早的版本,协议1和协议2是Pyhton2中引入的,协议3是Python3.0中引入的,协议4是Python3.4中引入的,每个协议都有其特点和适用范围。 下面我们来详细了解一下pickle模块的使用方法和各个协议的特点。 ## 基本用法 pickle模块提供了dumps、dump、loads和load四个函数,分别用来进行序列化和反序列化操作。其中dumps和loads函数可以直接将对象转换成字节流或将字节流转换成对象,而dump和load函数则可以将对象序列化到文件或从文件中反序列化对象。 ### 序列化 将Python对象转换成字节流的过程称为序列化,可以使用dumps函数实现: ```python import pickle data = {'name': 'Tom', 'age': 18, 'gender': 'male'} bytes_data = pickle.dumps(data) print(bytes_data) ``` 输出结果为: ``` b'\x80\x04\x95\x17\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x03Tom\x94\x8c\x03age\x94K\x12\x8c\x06gender\x94\x8c\x04male\x94u.' ``` 可以看到,data字典被转换成了一串二进制的字节流。 ### 反序列化 将字节流转换成Python对象的过程称为反序列化,可以使用loads函数实现: ```python import pickle bytes_data = b'\x80\x04\x95\x17\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x03Tom\x94\x8c\x03age\x94K\x12\x8c\x06gender\x94\x8c\x04male\x94u.' data = pickle.loads(bytes_data) print(data) ``` 输出结果为: ``` {'name': 'Tom', 'age': 18, 'gender': 'male'} ``` ### 文件操作 除了使用dumps和loads函数进行序列化和反序列化操作外,pickle模块还提供了dump和load函数用于将对象序列化到文件或从文件中反序列化对象。 将对象序列化到文件: ```python import pickle data = {'name': 'Tom', 'age': 18, 'gender': 'male'} with open('data.pkl', 'wb') as f: pickle.dump(data, f) ``` 从文件中反序列化对象: ```python import pickle with open('data.pkl', 'rb') as f: data = pickle.load(f) print(data) ``` ## 协议0 协议0是最早的版本,它使用ASCII码来表示序列化后的对象,因此序列化后的数据比较大。使用协议0时,可以指定文件打开模式为't',表示以文本模式打开文件: ```python import pickle data = {'name': 'Tom', 'age': 18, 'gender': 'male'} with open('data.pkl', 'wt') as f: pickle.dump(data, f, protocol=0) with open('data.pkl', 'rt') as f: data = pickle.load(f) print(data) ``` 输出结果为: ``` {'age': 18, 'gender': 'male', 'name': 'Tom'} ``` ## 协议1 协议1和协议2是Python2中引入的,它们使用更紧凑的二进制格式表示序列化后的对象。协议1可以指定文件打开模式为'wb',表示以二进制模式打开文件: ```python import pickle data = {'name': 'Tom', 'age': 18, 'gender': 'male'} with open('data.pkl', 'wb') as f: pickle.dump(data, f, protocol=1) with open('data.pkl', 'rb') as f: data = pickle.load(f) print(data) ``` 输出结果为: ``` {'name': 'Tom', 'age': 18, 'gender': 'male'} ``` ## 协议2 协议2是协议1的改进版本,它支持新的对象类型,如集合、字典等。在Python2中,协议2是默认使用的协议,如果不指定协议号,则使用协议2。 在Python3中,pickle模块默认使用协议3,但仍然可以使用协议2: ```python import pickle data = {'name': 'Tom', 'age': 18, 'gender': 'male'} with open('data.pkl', 'wb') as f: pickle.dump(data, f, protocol=2) with open('data.pkl', 'rb') as f: data = pickle.load(f) print(data) ``` 输出结果为: ``` {'name': 'Tom', 'age': 18, 'gender': 'male'} ``` ## 协议3 协议3是Python3.0中引入的,它支持更多的对象类型,如bytes、bytearray、set等。在Python3中,协议3是默认使用的协议,因此可以省略protocol参数: ```python import pickle data = {'name': 'Tom', 'age': 18, 'gender': 'male'} with open('data.pkl', 'wb') as f: pickle.dump(data, f) with open('data.pkl', 'rb') as f: data = pickle.load(f) print(data) ``` 输出结果为: ``` {'name': 'Tom', 'age': 18, 'gender': 'male'} ``` ## 协议4 协议4是Python3.4中引入的,它支持更多的对象类型,如memoryview、tuple等。协议4还支持从流中读取指定长度的数据,从而避免了一次性读取太多数据导致内存溢出的问题。 使用协议4时,需要将文件打开模式指定为'xb',表示以二进制模式打开文件,并且不能使用文本模式: ```python import pickle data = {'name': 'Tom', 'age': 18, 'gender': 'male'} with open('data.pkl', 'xb') as f: pickle.dump(data, f, protocol=4) with open('data.pkl', 'rb') as f: data = pickle.load(f) print(data) ``` 输出结果为: ``` {'name': 'Tom', 'age': 18, 'gender': 'male'} ``` ## 注意事项 在使用pickle模块时,需要注意以下几点: - 序列化和反序列化的对象必须是可序列化的,即不能包含不能序列化的对象。 - 序列化和反序列化的对象必须是相同的类型,否则可能会出现错误。 - 序列化和反序列化的对象必须是可信的,否则可能会被注入恶意代码。 - 不同协议之间的兼容性不同,不同协议之间的序列化和反序列化操作不一定是互逆的。因此,在使用不同协议时,需要注意协议号的兼容性和相应的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值