我被要求创建一个倒排索引并以多种方式(使用和不使用压缩)保存其二进制文件。
长话短说,我注意到与转换为dict
相比,使用list
表示占用的磁盘空间少得多。
示例:
dic = {
'w1': [1,2,3,4,5,6],
'w2': [2,3,4,5,6],
'w3': [3,4,5,6],
'w4': [4,5,6]
}
dic_list = list(dic.items())
import pickle
with open('dic.pickle', 'wb') as handle:
pickle.dump(dic, handle, protocol=pickle.HIGHEST_PROTOCOL)
with open('dic_list.pickle', 'wb') as handle:
pickle.dump(dic_list, handle, protocol=pickle.HIGHEST_PROTOCOL)
如果您同时检查两个文件的大小,则会注意到差异。
因此,我愿意知道它们之间的差异以及原因。任何其他信息将不胜感激
答案 0 :(得分:3)
dic_list
列表由更多对象组成。您有一个外部元组列表,每个元组都有一个键值对。每个值是另一个列表。这些元组是您需要更多空间的原因。
字典的pickle格式不必使用元组对象来存储键值对;预先已经知道字典由一系列对组成,因此您可以直接对每个对的键和值进行序列化,而不会产生包装元组对象的开销。
您可以使用pickletools
module分析泡菜数据;使用只有一个键值的更简单的字典,您已经可以看到区别:
>>> import pickle, pickletools
>>> pickletools.dis(pickle.dumps({'foo': 42}, protocol=pickle.HIGHEST_PROTOCOL))
0: \x80 PROTO 4
2: \x95 FRAME 12
11: } EMPTY_DICT
12: \x94 MEMOIZE (as 0)
13: \x8c SHORT_BINUNICODE 'foo'
18: \x94 MEMOIZE (as 1)
19: K BININT1 42
21: s SETITEM
22: . STOP
highest protocol among opcodes = 4
>>> pickletools.dis(pickle.dumps(list({'foo': 42}.items()), protocol=pickle.HIGHEST_PROTOCOL))
0: \x80 PROTO 4
2: \x95 FRAME 14
11: ] EMPTY_LIST
12: \x94 MEMOIZE (as 0)
13: \x8c SHORT_BINUNICODE 'foo'
18: \x94 MEMOIZE (as 1)
19: K BININT1 42
21: \x86 TUPLE2
22: \x94 MEMOIZE (as 2)
23: a APPEND
24: . STOP
如果您认为EMPTY_DICT
+ SETITEM
等同于EMPTY_LIST
+ APPEND
,则该流中唯一的真正区别在于添加了{{1 }} / TUPLE2
对操作码。这些操作码会占用额外的空间。
答案 1 :(得分:1)
dict
可以本地处理键值对,而list
必须使用单独的容器。
您的dict
是Dict[K, V]
的简单表示-成对加上某种结构。由于该结构仅是运行时,因此可以忽略存储。
{'a': 1, 'b': 2}
您的list
使用一个助手来配对,结果是List[Tuple[K,V]]
-配对加包装。由于需要使用包装器来重建对,因此存储时不能忽略它。
[('a', 1), ('b', 2)]
您也可以在腌菜堆中检查此情况。 list
转储包含用于其他元组的标记。
pickle.dumps({'a': 1, 'b': 2}, protocol=0)
(dp0 # <new dict>
Va # string a
p1
I1 # integer 1
sVb # <setitem key/value>, string b
p2
I2 # integer 2
s. # <setitem key/value>
pickle.dumps(list({'a': 1, 'b': 2}.items()), protocol=0)
(lp0 # <new list>
(Va # <marker>, string a
p1
I1 # integer 1
tp2 # <make tuple>
a(Vb # <append>, <marker>, string b
p3
I2 # integer 2
tp4 # <make tuple>
a. # <append>
虽然周围的dict
和list
都按成对的顺序存储,但对的存储方式却不同。对于dict
,仅键,值和停止符被平面存储。对于list
,每对还需要一个额外的tuple
。