Почему загрузка и дамп python pickle увеличивают размер объекта на диске?

У меня есть маринованный объект в файле с именем b1.pkl:

$ ls -l b*
-rw-r--r--  1 fireball  staff  64743950 Oct 11 15:32 b1.pkl

Затем я запускаю следующий код Python, чтобы загрузить объект и сбросить его в новый файл:

import numpy as np
import cPickle as pkl

fin = open('b1.pkl', 'r')
fout = open('b2.pkl', 'w')

x = pkl.load(fin)
pkl.dump(x, fout)

fin.close()
fout.close()

Файл, который создает этот код, более чем в два раза больше:

$ ls -l b*
-rw-r--r--  1 fireball  staff   64743950 Oct 11 15:32 b1.pkl
-rw-r--r--  1 fireball  staff  191763914 Oct 11 15:47 b2.pkl

Кто-нибудь может объяснить, почему новый файл намного больше исходного? Он должен содержать точно такую ​​же структуру.


person user1389890    schedule 11.10.2012    source источник
comment
был ли оригинальный рассол приготовлен по тому же протоколу?   -  person root    schedule 12.10.2012


Ответы (3)


Возможно, исходный рассол использовал какой-то другой протокол. Например, попробуйте указать protocol=2 в качестве аргумента ключевого слова для второго pickle.dump и протестируйте его снова. Бинарный рассол должен быть намного меньше по размеру.

person root    schedule 11.10.2012
comment
Я попытался указать протокол = 2 и, вуаля, результирующий файл имеет тот же размер, что и оригинал! - person user1389890; 12.10.2012

Скорее всего, ваш исходный b1.pkl был выделен с использованием более эффективного режима протокола (1 или 2). Таким образом, ваш файл начинается с меньшего размера.

Когда вы загружаетесь с помощью cPickle, он автоматически определяет для вас протокол из файла. Но когда вы снова выгрузите его с аргументами по умолчанию, он будет использовать протокол 0, который намного больше. Это делается для переносимости/совместимости. Вы должны явно запросить двоичный протокол.

import numpy as np
import cPickle

# random data
s = {}
for i in xrange(5000):
    s[i] = np.random.randn(5,5)

# pickle it out the first time with binary protocol
with open('data.pkl', 'wb') as f:
    cPickle.dump(s, f, 2)

# read it back in and pickle it out with default args
with open('data.pkl', 'rb') as f:
    with open('data2.pkl', 'wb') as o:
        s = cPickle.load(f)
        cPickle.dump(s, o)

$ ls -l
1174109 Oct 11 16:05 data.pkl
3243157 Oct 11 16:08 data2.pkl
person jdi    schedule 11.10.2012
comment
Когда я выгрузил оригинал, я использовал параметр рассола, равный -1. Видимо, это привело к более компактному представлению на диске. Я предполагаю, что это должно было заставить python использовать протокол = 2. - person user1389890; 12.10.2012

pkl.dump(x, fout, 2), вероятно, приведет к тому же размеру файла. Не указание версии протокола приведет к тому, что pickle будет использовать старую версию 0.

person Storstamp    schedule 11.10.2012