python-3.x - 协议 = 4 泡菜(python 3.7):加载具有相同键的字典时出现键错误
问题描述
我正在运行由 anaconda 在 Mac 上安装的 python3.7.9-64bit。
刚好遇到三个问题。
(虽然我写了 fix_import=False,但改变它并没有任何区别)
第一个问题,如果两个具有相同键的字典在一个文件中被腌制,加载第二个字典将引发 KeyError。
import pickle
d1={"a":1}# if two dicts has the same key
d2={"a":0}# does not matter what value is stored.
tmpf=open("deleteThis","wb")
pkl=pickle.Pickler(tmpf,protocol=4,fix_imports=False)
pkl.dump(d1)
pkl.dump(d2)
tmpf.close()
tmpf2=open("deleteThis","rb")
td1=pickle.load(tmpf2)
td2=pickle.load(tmpf2)# KeyError 1
第二个问题,虽然它可能与第一个有关,但如果第二个字典里面有一个字典D与第一个字典共享相同的键,那么D的键在加载时会改变。
d1={"a":1}
d2={"lll":{"a":2} }# this "a" will always be "lll"
tmpf=open("deleteThis","wb")
pkl=pickle.Pickler(tmpf,protocol=4,fix_imports=True)
pkl.dump(d1)
pkl.dump(d2)
tmpf.close()
tmpf2=open("deleteThis","rb")
td1=pickle.load(tmpf2)
td2=pickle.load(tmpf2)
print(td1,td2)# {'a': 1} {'lll': {'lll': 2}}
第三个问题,对我来说最具破坏性的,如果有一个字典 get pickle.dump 并且第二个转储的字典有一个共享相同密钥的字典列表,加载第二个字典raise KeyError。
d1={"b":0}
d2={ "l":[{"a":2} , {"a",3}] }
tmpf=open("deleteThis","wb")
pkl=pickle.Pickler(tmpf,protocol=4,fix_imports=True)
pkl.dump(d1)
pkl.dump(d2)
tmpf.close()
tmpf2=open("deleteThis","rb")
td1=pickle.load(tmpf2)
td2=pickle.load(tmpf2)# KeyError
这些问题只发生在pickle( protocol=4 )
.
Unknown about python>3.8
没有任何问题或protocol=3
protocl=5
解决方案
对您的问题的简短回答是用于pickle.Unpickler()
加载您的泡菜。
或者,不要使用pickle.Pickler()
. 而是用 or 写每个泡菜并用orpickle.dump()
读回。pickle.load()
pickle.Unpickler()
其中任何一个都可以解决您的问题。
我可以确认您描述的相同问题存在于 Python 3.9.1 中,适用于协议版本 4 和 5。
顺便说一句:请注意,{"a",3}
在您的最后一个示例中,是一个集合,而不是您想象的 dict。然而,同样的错误会发生。
问题是它Pickler
使用备忘录来缓存它已经腌制的数据。它通过避免多次存储相同的数据来使用它来节省结果文件的大小。备忘录在所有用Pickler
.
使用Unpickler
备忘录重建共享缓存数据的腌制对象。但是,pickle.load()
不使用备忘录,因此Pickler
在转储单个泡菜时可能无法找到备忘录的值。
下面是一些代码来演示:
import pickle
d1 = {"b": 0}
d2 = {"l": [{"a": 2}, {"a": 3}]}
with open("deleteThis", "wb") as tmpf:
pkl = pickle.Pickler(tmpf, protocol=4)
for obj in d1, d2:
pkl.dump(obj)
# reload objects with Unpickler - will work.
with open("deleteThis", "rb") as tmpf:
unpkl = pickle.Unpickler(tmpf)
print('Using Unpickler')
while True:
try:
print(unpkl.load())
except EOFError:
break
# reload objects with pickle.load - might work, but won't here.
with open("deleteThis", "rb") as tmpf:
print('Using pickle.load()')
while True:
try:
print(pickle.load(tmpf))
except EOFError:
break
它的输出:
使用 Unpickler {'b':0} {'l': [{'a': 2}, {'a': 3}]} 使用 pickle.load() {'b':0} 回溯(最近一次通话最后): 文件“/tmp/z.py”,第 26 行,在 打印(pickle.load(tmpf)) _pickle.UnpicklingError:在索引 6 处找不到备注值
推荐阅读
- java - 解决“多个 Jar 文件”又名“从多个位置扫描”
- android - viewpager2 与片段,其中 viewpager2 在销毁第一个父亲片段后也没有被销毁
- jquery - 如何使用 jquery 将“2020-10-08 09:38:08”转换为 2020 年 3 月格式
- simple-injector - 使用 Lifestyle.Singleton 时注册的 COM 接口无法解析
- algorithm - 使用重心将任何图形转换为平面的复杂性
- perl - 如何做 Perl 状态机(FSM)来解析比特流(字节序列)?
- flutter - 使用异步更新提供程序后,具有未来构建器的小部件不会删除小部件
- azure - 如何在 Azure 云计费导出中查找 ServiceName
- javascript - The "listener" argument must be of type function. Received undefined
- r - 在ggplot中设置ylims时如何删除小数位?