protocol=4 pickle (python 3.7): 使用内部相同的键加载字典时出现键盘错误
protocol=4 pickle (python 3.7): Keyerror when load dicts with the same key inside
我是 运行 python3.7.9-64 位,由 anaconda 安装在 Mac.
刚好遇到三个问题
(虽然我写的是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}}
第三个问题,对我来说最具破坏性的是,如果有一个 dict get pickle.dump 并且第二个转储的 dict 有一个共享相同密钥的 dicts 列表, 加载第二个字典 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 )
。
protocol=3
没问题
未知 python>3.8 或 protocl=5
您问题的简短回答是使用 pickle.Unpickler()
加载泡菜。
或者,不要使用 pickle.Pickler()
。而是用 pickle.dump()
写每个泡菜,然后用 pickle.load()
或 pickle.Unpickler()
.
回读
其中任何一个都可以解决您的问题。
我可以确认在 Python 3.9.1 协议版本 4 和 5 中都存在您描述的相同问题。
顺便说一句:请注意,您上一个示例中的 {"a",3}
是一个集合,而不是您所想的字典。然而还是会出现同样的错误。
问题是 Pickler
使用备忘录来缓存已腌制的数据。它通过避免多次存储相同的数据来使用它来节省生成文件的大小。备忘录在所有使用 Pickler
.
编写的泡菜之间共享
Unpickler
使用备忘录重建共享缓存数据的 pickled 对象。但是,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
它的输出是:
Using Unpickler
{'b': 0}
{'l': [{'a': 2}, {'a': 3}]}
Using pickle.load()
{'b': 0}
Traceback (most recent call last):
File "/tmp/z.py", line 26, in
print(pickle.load(tmpf))
_pickle.UnpicklingError: Memo value not found at index 6
我是 运行 python3.7.9-64 位,由 anaconda 安装在 Mac.
刚好遇到三个问题
(虽然我写的是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}}
第三个问题,对我来说最具破坏性的是,如果有一个 dict get pickle.dump 并且第二个转储的 dict 有一个共享相同密钥的 dicts 列表, 加载第二个字典 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 )
。
protocol=3
没问题
未知 python>3.8 或 protocl=5
您问题的简短回答是使用 pickle.Unpickler()
加载泡菜。
或者,不要使用 pickle.Pickler()
。而是用 pickle.dump()
写每个泡菜,然后用 pickle.load()
或 pickle.Unpickler()
.
其中任何一个都可以解决您的问题。
我可以确认在 Python 3.9.1 协议版本 4 和 5 中都存在您描述的相同问题。
顺便说一句:请注意,您上一个示例中的 {"a",3}
是一个集合,而不是您所想的字典。然而还是会出现同样的错误。
问题是 Pickler
使用备忘录来缓存已腌制的数据。它通过避免多次存储相同的数据来使用它来节省生成文件的大小。备忘录在所有使用 Pickler
.
Unpickler
使用备忘录重建共享缓存数据的 pickled 对象。但是,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
它的输出是:
Using Unpickler {'b': 0} {'l': [{'a': 2}, {'a': 3}]} Using pickle.load() {'b': 0} Traceback (most recent call last): File "/tmp/z.py", line 26, in print(pickle.load(tmpf)) _pickle.UnpicklingError: Memo value not found at index 6