【问题标题】:Given a pickle dump in python how to I determine the used protocol?给定 python 中的 pickle 转储,我如何确定使用的协议?
【发布时间】:2020-12-05 08:57:28
【问题描述】:

假设我有一个 pickle 转储 - 作为一个文件或只是作为一个字符串 - 我如何确定用于自动创建 pickle 转储的协议?

如果是这样,我是否需要阅读整个转储以找出协议,或者这可以在 O(1) 中实现吗?通过 O(1),我考虑了 pickle 字符串或文件开头的一些标题信息,其读出不需要处理整个转储。

非常感谢!

编辑:我对此有更新,显然下面给出的答案在 python 3.4 下并不总是有效。如果我只是简单地使用协议 1 腌制值 True,有时我只能恢复协议 0:-/

【问题讨论】:

  • 请注意,没有完整性或有效性约束,除了通过 unpickling 之外,您无法检查字符串是否是有效的 pickle 转储。这会产生一些后果:例如缓冲区对象是可拾取的,但结果字符串是不可拾取的。

标签: python pickle


【解决方案1】:

您可以使用picketools

with open('your_pickle_file', 'rb') as fin:
    op, fst, snd = next(pickletools.genops(fin))
    proto = op.proto

似乎 PROTO 标记仅作为协议为 2 或更大的第一个元素写入。否则,第一个元素是指示协议是 0 还是 1 的标记或元素。

更新到组合更多的土地:

pops = pickletools.genops(pickle_source)
proto = 2 if next(pops)[0].proto == 2 else int(any(op.proto for op, fst, snd in pops))

【讨论】:

  • 不会区分protocol=0protocol=1
  • @alko 我认为它确实......你有没有它没有的具体案例?
  • 是的,任何人都会这样做op, fst, snd = next(pickletools.genops(cPickle.dumps('123', protocol=0))),然后是{'MARK': 0,'PROTO': fst}.get(op.name, 1)
  • @SmCaterpillar 我认为这可能有点像兔子洞 :) 无论如何,如果它是 2 的原型,我敢肯定 2 的原型是第一个。否则,而不是取最大值,你在原型为 1 的情况下可能会短路(添加到答案中)。这意味着只有 proto 0 需要完整扫描。如果做不到这一点,我无法以简单的方式找到任何文档,所以可能会破解 Pickle.load 函数......这给了我另一个想法......
  • @SmCaterpillar bitbucket.org/python_mirrors/cpython-fullhistory/src/… 有很多信息......而且看起来具有协议版本的泡菜只是协议 2 的东西。所以,在那个版本之前,检查它是否是协议 0 的唯一方法是确保它不是协议 1 ......现在这几乎就是这个答案的作用......(哎呀)
【解决方案2】:

2020 年更新

我尝试了这里的方法(来自@JonClements 的回答和来自 cmets),但似乎没有一个给我正确的协议。

以下是可行的:

proto = None
op, fst, snd = next(pickletools.genops(data))
if op.name == 'PROTO':
    proto = fst

另类(不酷,因为它解开了整个事情):

out = io.StringIO()
pickletools.dis(data, out)
firstline = out.getvalue().splitlines()[0]
if ' PROTO ' in firstline:
    proto = re.sub(r'.*\s+', '', firstline)
    proto = int(proto)

应用程序:我想知道pandas.to_hdf() 中使用了哪些pickle 协议(如果使用了酸洗,情况并非总是如此),并且因为我不想分析HDF5 的整个结构文件,我使用MonkeyPatch 来监视pickle.loads() 被要求反序列化的内容。

无论谁通过 Google 搜索来到这里,这都是我的整个(笨拙的)设置:

__pickle_loads = pickle.loads


def mock_pickle_loads(data):
    global max_proto_found
    op, fst, snd = next(pickletools.genops(data))
    if op.name == 'PROTO':
        proto = fst
        max_proto_found = max(max_proto_found, proto)
    return __pickle_loads(data)


def max_pklproto_hdf(hdf_filename):
    global max_proto_found
    max_proto_found = -1
    with MonkeyPatch().context() as m:
        m.setattr(pickle, 'loads', mock_pickle_loads)
        try:
            pd.read_hdf(hdf_filename)
        except ValueError:
            pass
    return max_proto_found

【讨论】:

    猜你喜欢
    • 2017-10-01
    • 2014-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 2016-11-27
    相关资源
    最近更新 更多