Python pickle 协议选择?

Posted

技术标签:

【中文标题】Python pickle 协议选择?【英文标题】:Python pickle protocol choice? 【发布时间】:2014-06-28 05:58:22 【问题描述】:

我使用 python 2.7 并试图腌制一个对象。我想知道泡菜协议之间的真正区别是什么。

import numpy as np
import pickle

class Data(object):
  def __init__(self):
    self.a = np.zeros((100, 37000, 3), dtype=np.float32)

d = Data()
print("data size: ", d.a.nbytes / 1000000.0)
print("highest protocol: ", pickle.HIGHEST_PROTOCOL)
pickle.dump(d, open("noProt", "w"))
pickle.dump(d, open("prot0", "w"), protocol=0)
pickle.dump(d, open("prot1", "w"), protocol=1)
pickle.dump(d, open("prot2", "w"), protocol=2)


out >> data size:  44.4
out >> highest protocol:  2

然后我发现保存的文件在磁盘上有不同的大小:

noProt: 177.6MB prot0: 177.6MB prot1: 44.4MB prot2:44.4MB

我知道prot0 是人类可读的文本文件,所以我不想使用它。 我猜协议 0 是默认给定的。

我想知道协议 1 和 2 之间有什么区别,我有理由选择其中一个吗?

picklecPickle 哪个更好用?

【问题讨论】:

【参考方案1】:

使用支持读取数据的最低 Python 版本的最新协议。较新的协议版本支持新的语言功能并包括优化。

来自pickle module data format documentation:

目前有 6 种不同的协议可用于酸洗。使用的协议越高,读取生成的 pickle 所需的 Python 版本就越新。

协议版本 0 是原始的“人类可读”协议,向后兼容早期版本的 Python。 协议版本 1 是一种旧的二进制格式,也与早期版本的 Python 兼容。 协议版本 2 是在 Python 2.3 中引入的。它提供了更有效的new-style classes 酸洗。有关协议 2 带来的改进的信息,请参阅 PEP 307。 在 Python 3.0 中添加了协议版本 3。它明确支持 bytes 对象,并且不能被 Python 2.x 解压。这是 Python 3.0–3.7 中的默认协议。 在 Python 3.4 中添加了协议版本 4。它增加了对非常大的对象、腌制更多种类的对象以及一些数据格式优化的支持。它是从 Python 3.8 开始的默认协议。有关协议 4 带来的改进的信息,请参阅 PEP 3154。 在 Python 3.8 中添加了协议版本 5。它增加了对带外数据的支持和对带内数据的加速。有关协议 5 带来的改进的信息,请参阅 PEP 574。

来自 [pickle.Pickler(...) 类部分](

可选的protocol参数,一个整数,告诉pickler使用给定的协议;支持的协议是 0 到 HIGHEST_PROTOCOL。如果未指定,则默认为DEFAULT_PROTOCOL。如果指定负数,则选择HIGHEST_PROTOCOL

因此,当您希望支持使用 Python 3.4 或更高版本加载腌制数据时,请选择协议 4。如果您仍需要支持 Python 2.7,请选择协议 2,尤其是如果您正在使用自定义类派生自object(新式类)(现在任何现代代码都这样做)。

但是,如果您要与其他 Python 版本交换腌制数据,或者需要保持与旧 Python 版本的向后兼容性,则最简单的方法是坚持使用您可以使用的最高协议版本:

with open("prot2", 'wb') as pfile:
    pickle.dump(d, pfile, protocol=pickle.HIGHEST_PROTOCOL)

pickle.HIGHEST_PROTOCOL 将始终是当前 Python 版本的正确版本。因为这是二进制格式,所以一定要使用'wb'作为文件模式!

Python 3 不再区分 cPicklepickle,在使用 Python 3 时始终使用 pickle。它在后台使用已编译的 C 扩展。

如果你还在使用 Python 2,那么 cPicklepickle 大部分是兼容的,区别在于提供的 API。对于大多数用例,只需使用cPickle;它更快。再次引用documentation:

首先,cPickle 可以比 pickle 快 1000 倍,因为前者是在 C 中实现的。其次,在 cPickle 模块中,可调用对象 Pickler()Unpickler() 是函数,而不是类。这意味着您不能使用它们来派生自定义酸洗和取消酸洗子类。大多数应用程序不需要此功能,应该会从 cPickle 模块的性能大幅提升中受益。

【讨论】:

pickle 是 Python 3 中的 C 版本,Python 3.4 使用协议 3,即twice as fast as protocol 2。 @CeesTimmerman:前提是您不使用write individual small integers,从而阻碍取景速度的提高,否则速度会变慢。 :-) 这个答案是针对 Python 2 的,因为这就是 OP 所要求的。 我看到并添加了python-2.7 标签。不是比较 3 和 4 而不是 2 和 3 的基准吗? 请注意,在较新版本的 Python 中,如果未指定,协议将默认为 pickle.DEFAULT_PROTOCOL @Forensic_07:更新了报价;早就应该这样做了。【参考方案2】:

对于使用 Python 3 的用户,从 Python 3.5 开始,有五种可能的协议可供选择:

目前有 5 种不同的协议可用于酸洗。使用的协议越高,读取生成的 pickle 所需的 Python 版本越新 [doc]:

协议版本 0 是原始的“人类可读”协议,向后兼容早期版本的 Python。

协议版本 1 是一种旧的二进制格式,也与早期版本的 Python 兼容。

协议版本 2 是在 Python 2.3 中引入的。它提供了更有效的新型类的酸洗。请参阅 PEP 307 了解 有关协议 2 带来的改进的信息。 在 Python 3.0 中添加了协议版本 3。它对字节对象有明确的支持,并且不能被 Python 2.x 解压。这 是默认协议,推荐的协议时 需要与其他 Python 3 版本兼容。 在 Python 3.4 中添加了协议版本 4。它增加了对非常大的对象、腌制更多种类的对象和一些数据的支持 格式优化。有关有关信息,请参阅 PEP 3154 协议 4 带来的改进。 在 Python 3.8 中添加了协议版本 5。它增加了对带外数据的支持和对带内数据的加速。有关协议 5 带来的改进的信息,请参阅 PEP 574。

一般规则是您应该使用与您想要使用它的用途向后兼容的最高可能协议。因此,如果您希望它向后兼容 Python 2,那么协议版本 2 是一个不错的选择,如果您希望它向后兼容所有 Python 版本,那么版本 1 是不错的选择。如果您不关心向后兼容性,那么使用 pickle.HIGHEST_PROTOCOL 会自动为您的 Python 版本提供最高协议。

同样在 Python 3 中,导入 pickle 会自动导入 C 实现。

在兼容性方面需要注意的另一点是,默认情况下,协议 3 和 4 使用字符串的 unicode 编码,而早期的协议则不使用。因此,在 Python 3 中,如果您加载在 Python 2 中腌制的腌制文件,您可能必须显式指定编码才能正确加载它。

【讨论】:

以上是关于Python pickle 协议选择?的主要内容,如果未能解决你的问题,请参考以下文章

python里pickle是啥意思

python之pickle模块

python序列化与反序列化(json与pickle)

python——pickle模块的详解

python常用模块之json和pickle模块

Python标准库:pickle