为什么以下代码在作为脚本运行时会产生错误?在交互式 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/