4

大きな辞書 (28 MB) 'MyDict' がMyDict.pyファイルに保存されています。

ステートメントを実行すると:

from MyDict import MyDict

MemoryError例外がスローされます。

cPickleorshelveモジュールを使用してこの辞書にアクセスするにはどうすればよいですか。

MyDict.pyこのファイルをMyDict に、cPickleまたはshelveMyDict にアクセスせずに書き込むにはどうすればよいですか。

この 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)]]}
4

1 に答える 1

12

shelveここでは実際にかなり良い選択です。ディクショナリのように機能しますが、BDB (または同様の) キー値データベース ファイルに支えられており、Python がすべてのキャッシュなどを処理するため、すべてを一度にメモリにロードする必要はありません。

shelve ファイルの作成方法は次のとおりです。シェルフ キーは文字列でなければならないことに注意してください。また、最初に を作成してシェルフするのではなく、その場でシェルフを作成していることにも注意してくださいdictdictそうすれば、そもそも問題を引き起こしていた巨大なインメモリを構築するコストを回避できます。

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全体をメモリに読み込む必要があるため、おそらくそれほど良いアイデアではありません。巨大なリテラルを定義するモジュールをインポートするよりも、おそらく一時的なメモリとおそらくディスク容量をはるかに少なく使用しますが、それでも、巨大なメモリ内ハッシュテーブルを持つことは依然として問題になる可能性があります. ただし、いつでもテストして、うまく機能することを確認できます。

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)

これにより、47MB のファイルが作成されます。

次に、メイン アプリで使用するには:

import cPickle

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

保存してロードする必要がある他の永続化形式についても同じ基本的な問題が発生しpickleます。それは、自分で作成したカスタムであっても、JSON や YAML などの標準的なものであっても同じです。(もちろん、他のプログラム、特に他の言語との相互運用性が必要な場合は、JSON のようなものが適しています。) データベースを使用する方がよいでしょう。唯一の問題は、どのようなデータベースかということです。

anydbm型データベースの利点は、 (と行dict以外の) ロード/保存/アクセスの方法を気にせずに、 のように使用できることです。問題は、文字列を文字列にしかマップできないことです。opencloseanydbm

このモジュールは、各値をピクルしてshelve効果的にラップします。anydbmキーは文字列でなければなりませんが、値はほとんど何でもかまいません。したがって、キーが文字列であり、値から外部オブジェクトへの参照がない限り、dict.

その他のオプション (<code>sqlite3、さまざまな最新の nosql データベースなど) では、データへのアクセス方法、さらにはデータの整理方法を変更する必要があります。(「リストのリスト」は明確な ER モデルではありません。) もちろん、長期的には、これはより良い設計になる可能性があるため、本当にリレーショナル モデルを使用する必要があると思われる場合は、このアイデアを検討してください。


コメントから、@ekta は、 と の制限のいくつかが存在する理由を説明するように求めましdbmshelve

まず、dbm70年代にさかのぼります。8 ビットの文字列を文字列に簡単かつ効率的にマップできるデータベースは、当時は非常に大きな取引でした。また、すべての種類の値が文字列表現として格納されることもかなり一般的でした。そうでない場合は、現在のマシンでネイティブに値を表すバイトを格納するだけです。(XML、JSON、またはエンディアン スワッピングでさえ、当時のマシン、または少なくとも当時の考え方にはコストがかかりすぎた可能性があります。)

値の他のデータ型を処理するように拡張dbmすることは難しくありません。ハッシュしたり比較したりする必要はなく、ロスレスで保存および取得するだけです。非常に多種多様なタイプを処理でき、それほど非効率的ではなく、Python に付属しているため、そのためpickleに使用するのは理にかなっています。まさにそれを行います。pickleshelve

しかし、キーは別の話です。可逆可逆であるだけでなく、2 つの値が実際に等しい場合に限り、2 つの値が等しいバイトにエンコードされることを保証するエンコードが必要です。Python では、1 == True、しかし明らかpickle.dumps(1) != pickle.dumps(True)b'1' != b'True'、 などに注意してください。

その型のみを気にする場合、無損失かつ等価性を維持してバイトに変換できる型がたくさんあります。たとえば、Unicode 文字列の場合は、UTF-8 を使用してください。(実際にshelveは、それを処理します。) 32 ビットの符号付き整数の場合は、 を使用しますstruct.pack('>I')。3 つの文字列のタプルの場合は、UTF-8 にエンコードし、バックスラッシュでエスケープし、改行で結合します。等々。多くの特定のドメインについては、簡単な答えがあります。ほとんどのドメインで機能する汎用的な答えはありません。

したがって、 aを使用して、たとえば 3 つの UTF-8 文字列のタプルをキーとして使用する場合は、 (または)dbmの周りに独自のラッパーを記述できます。stdlib の多くのモジュールと同様に、ドキュメントにソースへのリンクがあるのは、便利なサンプル コードであり、使用可能な機能であることを目的としています。初心者でもフォーク、サブクラス化、またはラップして独自のカスタム キー エンコーディングを行う方法を理解できるほど単純です。( をラップする場合、それを にエンコードできるように、カスタム値を にエンコードする必要があることに注意してください。フォークするか、サブクラス化し、関連するメソッドをオーバーライドする場合は、代わりに直接エンコードすることができます。dbmshelveshelveshelvestrstrbytesbytesstruct.pack上に電話。これは、単純さ/読みやすさとパフォーマンスの両方で優れている可能性があります。)

于 2012-12-13T01:22:00.567 に答える