在介绍pickle
之前,复习一下eval
可以起到提神醒脑的作用,eval
是一个不太安全的东西,因为可以实现将字符串转为代码,并执行。
例如
>>> eval("print(1)")
1
如果乱用这个函数,就会把别人恶意写出的字符串代码执行,导致灾难性的后果。
pickle
可以用二进制表示python中的数据,并存储下来,而且能够重新读取。所以这也是一个不安全的功能,如果把一个pickle
暴露给别人,那万一遇到了坏人,同样有被植入恶意程序的风险。
加载和保存
pickle
中有两组加载和保存函数
加载 | 保存 | |
---|---|---|
交互文件 | dump | load |
交互字节码 | dumps | loads |
例如
x = [i**2 for i in range(10)]
import pickle
s = pickle.dumps(x)
print(s)
# b'\x80\x04\x95\x19\x00\x00\x00\x00\x00\x00\x00]\x94(K\x00K\x01K\x04K\tK\x10K\x19K$K1K@KQe.'
with open("test.pickle", 'wb') as f:
pickle.dump(x, f) # 将x存为f
将x
存储为文件之后,还可以重新打开
with open("test.pickle", 'rb') as f:
y = pickle.load(f)
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
注意事项
除了不够安全之外,使用pickle另外一个需要注意的是,到目前为止,pickle已经有6个版本,其中v0
和v1
版本过于古老,基本没什么能用的地方;v3
以后的版本则不再支持Python2.x
,下表列出了自v2
到v5
的一些更新特性
引入版本 | 文档 | ||
---|---|---|---|
v2 | Python 2.3 | PEP307 | 为 新式类 提供了更高效的封存机制。 |
v3 | Python 3.0 | 显式地支持字节对象 为Python 3.0-3.7 的默认协议。 | |
v4 | Python 3.4 | PEP 3154 | 支持存储较大对象,并针对数据格式进行了优化 Python 3.8使用的默认协议。 |
v5 | Python 3.8 | PEP 574 | 增加对带外数据的支持,并可加速带内数据处理 |
理论上讲,pickle
可以封装一切python对象,所以对于一些比较耗时的算法,可以通过pickle
来保存当前的状态。
但要注意的是,尽管pickle
可以封存函数,但并不能封存匿名函数
>>> test = lambda x : x
>>> s = pickle.dumps(test)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function <lambda> at 0x00000207E76C7F70>: attribute lookup <lambda> on __main__ failed
>>> def test(x):
... print(x)
...
>>> s = pickle.dumps(test)
>>> print(s)
b'\x80\x04\x95\x15\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04test\x94\x93\x94.'
其他包库的支持
在pandas
中提供了to_pickle
,可将数据帧或系列转化为pickle
。