为什么以下代码在作为脚本运行时会产生错误?在交互式 shell(剪切和粘贴)中运行时不会产生错误。

import cPickle as pickle

val1 = dict(fooblah=[], xy=[])
pickval1 = pickle.dumps(val1, protocol=2)

val2 = pickle.loads(pickval1)
assert val1 == val2

pickval2 = pickle.dumps(val2, protocol=2)
assert pickval1 == pickval2, (pickval1, pickval2)

pickle 的区别如下:
$ python /tmp/picklefun.py
Traceback (most recent call last):
  File "/tmp/picklefun.py", line 10, in <module>
    assert pickval1 == pickval2, (pickval1, pickval2)
AssertionError: ('\x80\x02}q\x01(U\x07fooblahq\x02]U\x02xyq\x03]u.',
                 '\x80\x02}q\x01(U\x07fooblah]U\x02xy]u.')

最佳答案

如果更换线路

val1 = dict(fooblah=[], xy=[])


exec "val1 = dict(fooblah=[], xy=[])"

然后断言再次通过。

为什么??答案就在 cPickle 的奥秘深处。它有一个优化,可以查看某些对象的引用计数器是否小于 2,并在这种情况下避免使用几个字节(通常用于检测循环或同一可能大字符串的多次出现)。这是关于字符串对象“fooblah”和“xy”。在 exec 或交互式运行的情况下,当你pickle 时,对字符串的唯一引用在字典中;引用计数器是 1,所以 cPickle 避免了几个字节。但是如果你把这个例子写成一个模块,那么这个模块在那个时候还活着,它会保存另一个对用作常量的字符串的引用。

编辑 以澄清:第二次我们 pickle 时,我们将 pickle 一个字典,该字典始终具有来自 unpickling 的新键 - 引用计数器 1。因此,当且仅当引用计数器 1 的键为第一次。

关于python - 不一致的 cPickle,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16429916/

10-11 10:33