Дисковое пространство - словарь Python против списка - PullRequest
0 голосов
/ 19 ноября 2018

Меня попросили создать инвертированный индекс и сохранить его двоичный файл несколькими способами (со сжатием и без).

Короче говоря, я заметил, что использование представления 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 голосов
/ 19 ноября 2018

A 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 для каждой пары.

0 голосов
/ 19 ноября 2018

Список dic_list состоит из больше объектов .У вас есть внешний список кортежей, каждый кортеж пара ключ-значение.Каждое значение - это другой список.Эти кортежи - причина, по которой вам нужно больше места.

Формат словаря не должен использовать объекты кортежей для хранения пар ключ-значение;уже заранее известно, что словарь состоит из серии пар, поэтому вы можете сериализовать ключ и значение для такой пары напрямую, без дополнительных затрат на объект оберточного кортежа.

Вы можете анализировать данные консервирования с помощью pickletools модуль ;используя более простой словарь с одним ключевым значением, вы уже можете увидеть разницу:

>>> 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,тогда единственная реальная разница в этом потоке заключается в добавлении пары кодов операций TUPLE2 / MEMOIZE.Это те коды операций, которые занимают дополнительное место.

...