# 11.4. `shelve` --- Python オブジェクトの永続化

ソースコード: Lib/shelve.py

"シェルフ (shelf, 棚)" は辞書に似た永続性を持つオブジェクトです。 "dbm" データベースとの違いは、シェルフの値 (キーではありません！) は実質上どんな Python オブジェクトにも --- `pickle` モジュールが扱えるなら何でも --- できるということです。これにはほとんどのクラスインスタンス、再帰的なデータ型、沢山の共有されたサブオブジェクトを含むオブジェクトが含まれます。キーは通常の文字列です。

`shelve.``open`(filename, flag='c', protocol=None, writeback=False)

  永続的な辞書を開きます。指定された filename は、根底にあるデータベースの基本ファイル名となります。副作用として、 filename には拡張子がつけられる場合があり、ひとつ以上のファイルが生成される可能性もあります。デフォルトでは、根底にあるデータベースファイルは読み書き可能なように開かれます。オプションの flag パラメータは `anydbm.open()` における flag パラメータと同様に解釈されます。

  デフォルトでは、値を整列化する際にはバージョン 0 の pickle 化が用いられます。pickle 化プロトコルのバージョンは protocol パラメータで指定することができます。pickle 化プロトコルについては `pickle` のドキュメントを参照してください。

  バージョン 2.3 で変更: protocol パラメータが追加されました。

  Python の意味論により、シェルフには永続的な辞書の可変エントリがいつ変更されたかを知る術がありません。 デフォルトでは、変更されたオブジェクトはシェルフに代入されたとき だけ 書き込まれます (例 参照)。 オプションの writeback パラメータが `True` に設定されている場合は、アクセスされたすべてのエントリはメモリ上にキャッシュされ、 `sync()` および `close()` を呼び出した際に書き戻されます; この機能は永続的な辞書上の可変の要素に対する変更を容易にしますが、多数のエントリがアクセスされた場合、膨大な量のメモリがキャッシュのために消費され、アクセスされた全てのエントリを書き戻す (アクセスされたエントリが可変であるか、あるいは実際に変更されたかを決定する方法は存在しないのです) ために、ファイルを閉じる操作が非常に低速になります。

  ファイルオブジェクト同様に、shelve オブジェクトは永続データが確実にディスクにフラッシュされるように、明示的にクローズすべきです。

警告

`shelve` モジュールは裏で `pickle` を使っているので、信頼できないソースからシェルフを読み込むのは危険です。 pickle と同じく、 shelf の読み込みでも任意のコードを実行できるからです。

シェルフオブジェクトは辞書がサポートするほとんどのメソッドをサポートしています。これにより、辞書ベースのスクリプトから永続的な記憶媒体を必要とするスクリプトに容易に移行できるようになります。

Python 3 への移行用メソッド (`viewkeys()`, `viewvalues()`, `viewitems()`) はサポートされていないことに注意してください。

追加でサポートされるメソッドが二つあります:

`Shelf.``sync`()

  シェルフが writeback を `True` にセットして開かれている場合に、キャッシュ中の全てのエントリを書き戻します。また可能な場合は、キャッシュを空にしてディスク上の永続的な辞書を同期します。このメソッドはシェルフを `close()` によって閉じるとき自動的に呼び出されます。

`Shelf.``close`()

  永続的な 辞書 オブジェクトを同期して閉じます。既に閉じられているシェルフに対して呼び出すと `ValueError` を出し失敗します。

参考

通常の辞書に近い速度をもち、いろいろなストレージフォーマットに対応した、 永続化辞書のレシピ 。

## 11.4.1. 制限事項

- どのデータベースパッケージが使われるか (例えば `dbm` 、 `gdbm` 、`bsddb`) は、どのインタフェースが利用可能かに依存します。従って、データベースを `dbm` を使って直接開く方法は安全ではありません。データベースはまた、 `dbm` が使われた場合 (不幸なことに) その制約に縛られます --- これはデータベースに記録されたオブジェクト (の pickle 化された表現) はかなり小さくなければならず、キー衝突が生じた場合に、稀にデータベースを更新することができなくなることを意味します。
- `shelve` モジュールは、シェルフに置かれたオブジェクトの 並列した 読み出し/書き込みアクセスをサポートしません (複数の同時読み出しアクセスは安全です)。あるプログラムが書き込みのために開かれたシェルフを持っているとき、他のプログラムはそのシェルフを読み書きのために開いてはいけません。この問題を解決するために Unix のファイルロック機構を使うことができますが、この機構は Unix のバージョン間で異なり、使われているデータベースの実装について知識が必要となります。

class `shelve.``Shelf`(dict, protocol=None, writeback=False)

  `UserDict.DictMixin` のサブクラスで、pickle 化された値を dict オブジェクトに保存します。

  デフォルトでは、値を整列化する際にはバージョン 0 の pickle 化が用いられます。pickle 化プロトコルのバージョンは protocol パラメータで指定することができます。pickle 化プロトコルについては `pickle` のドキュメントを参照してください。

  バージョン 2.3 で変更: protocol パラメータが追加されました。

  writeback パラメータが `True` に設定されていれば、アクセスされたすべてのエントリはメモリ上にキャッシュされ、ファイルを閉じる際に dict に書き戻されます; この機能により、可変のエントリに対して自然な操作が可能になりますが、さらに多くのメモリを消費し、辞書をファイルと同期して閉じる際に長い時間がかかるようになります。

class `shelve.``BsdDbShelf`(dict, protocol=None, writeback=False)

  `Shelf` のサブクラスで、 `first()`, `next()`, `previous()`, `last()`, `set_location()` メソッドを公開しています。これらのメソッドは `bsddb` モジュールでは利用可能ですが、他のデータベースモジュールでは利用できません。コンストラクタに渡された dict オブジェクトは上記のメソッドをサポートしていなくてはなりません。通常は、 `bsddb.hashopen()`, `bsddb.btopen()` または `bsddb.rnopen()` のいずれかを呼び出して得られるオブジェクトが条件を満たしています。オプションの protocol および writeback パラメタは `Shelf` クラスにおけるパラメタと同様に解釈されます。

class `shelve.``DbfilenameShelf`(filename, flag='c', protocol=None, writeback=False)

  `Shelf` のサブクラスで、辞書に似たオブジェクトの代わりに filename を受理します。根底にあるファイルは `anydbm.open()` を使って開かれます。デフォルトでは、ファイルは読み書き可能な状態で開かれます。オプションの flag パラメータは `open()` 関数におけるパラメータと同様に解釈されます。オプションの protocol および writeback パラメータは `Shelf` クラスにおけるパラメータと同様に解釈されます。

## 11.4.2. 例

インタフェースは以下のコードに集約されています (`key` は文字列で、`data` は任意のオブジェクトです):

    import shelve

    d = shelve.open(filename) # open -- file may get suffix added by low-level
                              # library

    d[key] = data   # store data at key (overwrites old data if
                    # using an existing key)
    data = d[key]   # retrieve a COPY of data at key (raise KeyError if no
                    # such key)
    del d[key]      # delete data stored at key (raises KeyError
                    # if no such key)
    flag = d.has_key(key)   # true if the key exists
    klist = d.keys() # a list of all existing keys (slow!)

    # as d was opened WITHOUT writeback=True, beware:
    d['xx'] = range(4)  # this works as expected, but...
    d['xx'].append(5)   # *this doesn't!* -- d['xx'] is STILL range(4)!

    # having opened d without writeback=True, you need to code carefully:
    temp = d['xx']      # extracts the copy
    temp.append(5)      # mutates the copy
    d['xx'] = temp      # stores the copy right back, to persist it

    # or, d=shelve.open(filename,writeback=True) would let you just code
    # d['xx'].append(5) and have it work as expected, BUT it would also
    # consume more memory and make the d.close() operation slower.

    d.close()       # close it

参考

`anydbm` モジュール
  `dbm` スタイルの汎用的なインタフェース
`bsddb` モジュール
  BSD `db` データベースインタフェース。
`dbhash` モジュール
  `bsddb` をラップする薄いレイヤで、他のデータベースモジュールのように関数 `open()` を提供しています。
`dbm` モジュール
  標準の Unix データベースインタフェース。
`dumbdbm` モジュール
  `dbm` インタフェースの移植性のある実装。
`gdbm` モジュール
  `dbm` インタフェースに基づいた GNU データベースインタフェース。
`pickle` モジュール
  `shelve` によって使われるオブジェクト整列化機構。
`cPickle` モジュール
  `pickle` の高速版。