Pickle против хранения больших словарей на полке в Python

Если я сохраняю большой каталог в виде файла pickle, значит ли это, что загрузка его через cPickle будет занимать всю память одновременно?

Если да, то существует ли межплатформенный способ получить что-то вроде pickle, но получить доступ к каждой записи по одному ключу в элементе (т. е. избегать загрузки всего словаря в память и загружать каждую запись только по имени)? Я знаю, что shelve должен это делать: он такой же портативный, как pickle?


person Community    schedule 03.02.2013    source источник
comment
pickle — уязвимость безопасности   -  person Joran Beasley    schedule 03.02.2013
comment
содержимое в конечном итоге получает exec'd (или eval'd) при распаковке   -  person Joran Beasley    schedule 03.02.2013
comment
shelve использует некоторую разновидность базы данных DBM для хранения маринованных объектов. Он должен быть как минимум таким же переносимым, как pickle.   -  person chepner    schedule 03.02.2013
comment
Хотя @JoranBeasley технически корректен, соленья чрезвычайно полезны и безопасны, когда вы пишете их сами. НЕ ПРИНИМАЙТЕ соленья из ненадежных источников, но можете использовать их для сериализации ваших собственных данных.   -  person Kirk Strauser    schedule 03.02.2013
comment
@JoranBeasley shelve также подвержен тем же уязвимостям безопасности, что и pickle, поскольку его поддерживает pickle.   -  person Steven Mercatante    schedule 27.08.2014


Ответы (2)


Я знаю, что полка должна делать это: это так же портативно, как рассол?

Да. shelve является частью Стандартная библиотека Python и написана на Python.

Редактировать

Итак, если у вас большой словарь:

bigd = {'a': 1, 'b':2, # . . .
}

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

import shelve

myShelve = shelve.open('my.shelve')
myShelve.update(bigd)
myShelve.close()

Позже вы сможете:

import shelve

myShelve = shelve.open('my.shelve')
value = myShelve['a']
value += 1
myShelve['a'] = value

В основном вы обращаетесь с объектом полки как с dict, но элементы хранятся на диске (как отдельные соленья) и считываются по мере необходимости.

Если ваши объекты можно хранить в виде списка свойств, то sqlite может быть хорошая альтернатива. Полки и соленья удобны, но доступны только для Python, а база данных sqlite может читаться с большинства языков.

person jimhark    schedule 03.02.2013
comment
Так правда ли, что pickle всегда загружает все замаринованные объекты в память? - person ; 03.02.2013
comment
Полка не кроссплатформенная - person avocado; 05.07.2014
comment
Разве вам не нужно закрывать полку, чтобы изменения были записаны на диск? - person AlwaysLearning; 04.01.2017
comment
@AlwaysLearning, в документации Python 2.x конкретно говорится: как и файловые объекты, объекты полки должны быть закрыты явно, чтобы гарантировать, что постоянные данные будут сброшены на диск. См. docs.python.org/2/library/shelve.html. - person jimhark; 05.01.2017
comment
Ответ на вопрос это так же портативно, как рассола? нет. Например, на моей машине он выбирает поддержку dbhash. Если я создам файл БД и перенесу его на другую машину, где dbhash недоступен, shelve.open(..) не удастся использовать этот файл. В настоящее время единственным переносимым вариантом чистого Python является dumbdbm. Чтобы сделать переносную полку, ее необходимо настроить для явного использования dumbdbm (что не является значением по умолчанию). - person Evgeni Sergeev; 12.12.2017

Если вам нужен более надежный модуль, чем shelve, вы можете взглянуть на klepto. klepto создан для обеспечения словарного интерфейса к независимому от платформы хранилищу на диске или в базе данных и предназначен для работы с большими данными.

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

>>> d = dict(zip('abcde',range(5)))
>>> d['f'] = max
>>> d['g'] = lambda x:x**2
>>> 
>>> import klepto
>>> help(klepto.archives.dir_archive)       

>>> print klepto.archives.dir_archive.__new__.__doc__
initialize a dictionary with a file-folder archive backend

    Inputs:
        name: name of the root archive directory [default: memo]
        dict: initial dictionary to seed the archive
        cached: if True, use an in-memory cache interface to the archive
        serialized: if True, pickle file contents; otherwise save python objects
        compression: compression level (0 to 9) [default: 0 (no compression)]
        memmode: access mode for files, one of {None, 'r+', 'r', 'w+', 'c'}
        memsize: approximate size (in MB) of cache for in-memory compression

>>> a = klepto.archives.dir_archive(dict=d)
>>> a
dir_archive('memo', {'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3, 'g': <function <lambda> at 0x102f562a8>, 'f': <built-in function max>}, cached=True)
>>> a.dump()
>>> del a

Теперь, когда все данные находятся на диске, давайте выберем, какие из них мы хотим загрузить в память. b — это словарь в памяти, а b.archive отображает коллекцию файлов в виде словаря.

>>> b = klepto.archives.dir_archive('memo')
>>> b
dir_archive('memo', {}, cached=True)
>>> b.keys()   
[]
>>> b.archive.keys()
['a', 'c', 'b', 'e', 'd', 'g', 'f']
>>> b.load('a')
>>> b
dir_archive('memo', {'a': 0}, cached=True)
>>> b.load('b')
>>> b.load('f')
>>> b.load('g')
>>> b['g'](b['f'](b['a'],b['b']))
1

klepto также предоставляет такой же интерфейс для sql архива.

>>> print klepto.archives.sql_archive.__new__.__doc__
initialize a dictionary with a sql database archive backend

    Connect to an existing database, or initialize a new database, at the
    selected database url. For example, to use a sqlite database 'foo.db'
    in the current directory, database='sqlite:///foo.db'. To use a mysql
    database 'foo' on localhost, database='mysql://user:pass@localhost/foo'.
    For postgresql, use database='postgresql://user:pass@localhost/foo'. 
    When connecting to sqlite, the default database is ':memory:'; otherwise,
    the default database is 'defaultdb'. If sqlalchemy is not installed,
    storable values are limited to strings, integers, floats, and other
    basic objects. If sqlalchemy is installed, additional keyword options
    can provide database configuration, such as connection pooling.
    To use a mysql or postgresql database, sqlalchemy must be installed.

    Inputs:
        name: url for the sql database [default: (see note above)]
        dict: initial dictionary to seed the archive
        cached: if True, use an in-memory cache interface to the archive
        serialized: if True, pickle table contents; otherwise cast as strings

>>> c = klepto.archives.sql_archive('database')
>>> c.update(b)
>>> c
sql_archive('sqlite:///database', {'a': 0, 'b': 1, 'g': <function <lambda> at 0x10446b1b8>, 'f': <built-in function max>}, cached=True)
>>> c.dump()

Где теперь те же объекты на диске есть и в sql-архиве. Мы можем добавлять новые объекты в любой архив.

>>> b['x'] = 69
>>> c['y'] = 96
>>> b.dump('x')
>>> c.dump('y')

Получите klepto здесь: https://github.com/uqfoundation

person Mike McKerns    schedule 15.09.2015
comment
Обратите внимание, что я автор klepto - person Mike McKerns; 15.09.2015