2012-12-13 2 views
2

У меня есть большой словарь (28 МБ) 'MyDict', хранящийся в файле MyDict.py.Как хранить большой словарь?

Если я исполняю заявление:

from MyDict import MyDict 

MemoryError исключение.

Как я могу получить доступ к этому словарю с помощью cPickle или shelve модулей.

Как я могу записать этот файл MyDict.py в cPickle или shelve без доступа к MyDict.

Этот MyDict генерируется путем записи в файл. Вот ключ-значение пары из словаря:

{"""ABCD""" : [[(u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85), (u'2012-03-03', 86.0, 87.95, 85.55, 86.2, 30587.0, 86.2), (u'2011-03-23', 36.9, 36.9, 35.25, 36.1, 456.0, 36.1)], 
    [(u'2011-03-18', 37.0, 38.0, 36.5, 36.5, 861.0, 36.5), (u'2012-03-03', 86.0, 87.95, 85.55, 86.2, 30587.0, 86.2), (u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85)], 
    [(u'2011-03-16', 37.0, 37.9, 36.3, 36.7, 3876.0, 36.7), (u'2012-03-03', 86.0, 87.95, 85.55, 86.2, 30587.0, 86.2), (u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85)], 
    [(u'2010-12-09', 40.5, 41.95, 36.3, 36.75, 42943.0, 36.75), (u'2011-10-26', 67.95, 71.9, 66.45, 70.35, 180812.0, 70.35), (u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85)], 
    [(u'2009-01-16', 14.75, 15.0, 14.0, 14.15, 14999.0, 14.05), (u'2010-01-11', 50.0, 52.8, 49.0, 50.95, 174826.0, 50.95), (u'2009-01-27', 14.3, 15.0, 13.9, 14.15, 3862.0, 14.15)]]} 
+1

Вы пытались хранить большой словарь в отдельном текстовом файле с помощью JSON? –

+0

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

+0

Можете ли вы выставить отрывок из этого файла? В зависимости от того, насколько близок файл к действительному JSON, вы можете открыть файл для чтения и проанализировать его итеративно, чтобы записать значения в другой файл, который станет более доступным в будущем. –

ответ

8

shelve на самом деле очень хороший выбор здесь. Он действует точно так же, как словарь, но подкрепляется BDB (или аналогичным) файлом базы данных ключевого значения, а Python будет обрабатывать все кэширование и т. Д., Поэтому ему не нужно сразу загружать все это в память.

Вот как создать файл полки. Обратите внимание, что полные ключи должны быть строками. Также обратите внимание, что я создаю полку на месте, вместо того, чтобы сначала создавать dict и откладывать ее. Таким образом, вы избегаете затрат на создание этого гигантского в памяти dict, что в первую очередь вызывает проблемы.

from contextlib import closing 
import shelve 

def makedict(shelf): 
    # Put the real dict-generating code here, obviously 
    for i in range(500000); 
     shelf[str(i)] = i 

with closing(shelve.open('mydict.shelf', 'c')) as shelf: 
    makedict(shelf) 

И использовать его, на самом деле не читать; оставить его в качестве полки на диске:

from contextlib import closing 
import shelve 

with closing(shelve.open('mydict.shelf')) as d: 
    # Put all your actual work here. 
    print len(d) 

Если словарю используя код не может легко вписаться в рамки, заменить with заявление с простым open, и явно close, когда вы закончите.

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

Вот как создать файл рассола. Обратите внимание, что вы можете использовать (почти) все, что хотите, в качестве ключа, а не только строки. Тем не менее, вы должны построить целое dict, прежде чем сможете pickle его.

import cPickle 

def makedict(): 
    # Put the real dict-generating code here, obviously 
    return {i:i for i in range(500000)} 

with open('mydict.pickle', 'wb') as f: 
    cPickle.dump(d, f, -1) 

Это создает файл размером 47 МБ.

Теперь, чтобы использовать его в главном приложении:

import cPickle 

def loaddict(): 
    with open('mydict.pickle', 'rb') as f: 
     return cPickle.load(f) 

Те же основные проблемы с pickle пойти на любой другой формат сохраняемости, который должен быть сохранен и загружен-ли что-то обычай, что вы пишете сами, или что-то стандартное, как JSON или YAML. (Конечно, если вам нужна совместимость с другими программами, особенно на других языках, что-то вроде JSON - это путь.) Вам лучше с базой данных; вопрос только в том, какая база данных.

Преимущество базы данных anydbm типа является то, что вы можете использовать его, как если бы это было в dict, не заботясь о том, как загрузить/сохранить/доступ к нему (кроме open и close линий). Проблема с anydbm заключается в том, что она позволяет отображать строки только для строк.

Модуль shelve эффективно обертывает anydbm, с травлением каждого значения. Ваши ключи все равно должны быть строками, но ваши значения могут быть почти любыми. Так как ваши ключи являются строками, и у вас нет ссылок со значений на внешние объекты, это довольно прозрачная замена для замены dict.

Другие варианты - sqlite3, различные современные базы данных nosql и т. Д. - требуют, чтобы вы изменили способ доступа к данным и даже способ их организации. («Список списков» не является четкой моделью ER.) Конечно, в конечном итоге это может привести к лучшему дизайну, поэтому, если вы думаете, что действительно хотите использовать реляционную модель, рассмотрите эту идею.


Из комментариев, @ekta хотел, чтобы объяснить, почему некоторые ограничения на dbm и shelve существуют.

Во-первых, dbm восходит к 70-м годам. База данных, которая могла бы отображать 8-битные строки в строки просто и эффективно, была довольно сложной сделкой. Было также довольно распространено, что значения всех видов должны храниться в виде их строкового представления, или, если это не так, а затем просто хранить байты, которые представляют собой значение на месте на текущем компьютере. (XML, JSON или даже подгонка индексации, возможно, были слишком дорогими для машин в день или, по крайней мере, для размышлений дня.)

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

Но ключи - это совсем другая история. Вам нужна кодировка, которая не только без потерь обратима, но также гарантирует, что два значения будут кодироваться в равные байты, если и только если они на самом деле равны. Имейте в виду, что в Python, 1 == True, но, очевидно, pickle.dumps(1) != pickle.dumps(True)b'1' != b'True' и т.д.

Есть много типов, которые могут быть без потерь и равенство-preservingly преобразуются в байт, если вы заботитесь только о том, что тип. Например, для строк Unicode просто используйте UTF-8. (Фактически, shelve позаботится об этом для вас.) Для 32-разрядных целых чисел со знаком используйте struct.pack('>I'). Для кортежей из трех строк, кодируйте UTF-8, обратную косую черту-escape и присоединяйте их к символам новой строки. И так далее. Для многих конкретных доменов есть простой ответ; для большинства доменов нет ответа общего назначения.

Итак, если вы хотите использовать dbm использовать, скажем, кортежи из трех UTF-8 строк в качестве ключей, вы можете написать свою собственную обертку вокруг dbm (или shelve). Как и во многих модулях в stdlib, shelve призван служить полезным примером кода, а также полезной функцией, поэтому у the docs есть ссылка на the source. Это достаточно просто, чтобы новичок должен был понять, как его разветвить, подклассировать или обернуть, чтобы выполнить собственную кодировку ключа.(Обратите внимание: если вы обернете shelve, вам нужно будет закодировать ваши пользовательские значения до str, чтобы он мог кодировать это str в bytes, если вы его переделаете или подклассируете и переопределите соответствующие методы, вы можете вместо этого кодировать непосредственно bytes - например, что struct.pack вызывается выше. Это может быть лучше как для простоты/удобочитаемости, так и для производительности.)

+0

Я думаю, что проблема заключается не столько в том, как использовать cPickle (хотя это возможно), а в том, что он получает ошибку памяти, прежде чем он сможет добраться до вашей строки 'return'. Кроме того, вам не нужен контекстный lib для использования 'with' и' shelve'. Они предоставляют '__enter__' и' __exit__' самостоятельно. –

+0

@ g.d.d.c: объекты 'shelve' не предоставляют' __exit__' в 2.7 или 3.3, поэтому вам необходимо _ contextlib'. Но для вашего другого аспекта вы правы, вероятно, имеет смысл делать полку напрямую, чем создавать «dict», а затем «откладывать» ее. Я отредактирую ответ. – abarnert

+0

@ g.d.d.c делает точку. У меня есть словарь, значения которого - список списков. Я создал этот словарь, записав в файл. – Vinod

Смежные вопросы