磁盘空间-Python字典与列表

时间:2018-11-19 14:14:38

标签: python python-3.x list dictionary pickle

我被要求创建一个倒排索引并以多种方式(使用和不使用压缩)保存其二进制文件。

长话短说,我注意到与转换为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)

如果您同时检查两个文件的大小,则会注意到差异。

因此,我愿意知道它们之间的差异以及原因。任何其他信息将不胜感激

2 个答案:

答案 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必须使用单独的容器。

您的dictDict[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>

虽然周围的dictlist都按成对的顺序存储,但对的存储方式却不同。对于dict,仅键,值和停止符被平面存储。对于list,每对还需要一个额外的tuple