12.3. "shelve" --- Python オブジェクトの永続化
**********************************************

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

======================================================================

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

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

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

   デフォルトでは、値を整列化する際にはバージョン 3 の pickle 化が用い
   られます。pickle 化プロトコルのバージョンは *protocol* パラメータで
   指定することができます。

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

   注釈: シェルフが自動的に閉じることに依存しないでください; それが
     もう必 要ない場合は常に "close()" を明示的に呼ぶか、
     "shelve.open()" を コンテキストマネージャとして使用してください:

        with shelve.open('spam') as db:
            db['eggs'] = 'eggs'

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

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

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

Shelf.sync()

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

Shelf.close()

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

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


12.3.1. 制限事項
================

* どのデータベースパッケージが使われるか (例えば "dbm.ndbm" 、
  "dbm.gnu") は、どのインタフェースが利用可能かに依存します。従って、
  データベースを "dbm"  を使って直接開く方法は安全ではありません。デー
  タベースはまた、 "dbm" が使われた場合 (不幸なことに) その制約に縛ら
  れます --- これはデータベースに記録されたオブジェクト (の pickle 化
  された表現) はかなり小さくなければならず、キー衝突が生じた場合に、稀
  にデータベースを更新することができなくなることを意味します。

* "shelve" モジュールは、シェルフに置かれたオブジェクトの *並列した*
  読み出し/書き込みアクセスをサポートしません (複数の同時読み出しアク
  セスは安全です)。あるプログラムが書き込みのために開かれたシェルフを
  持っているとき、他のプログラムはそのシェルフを読み書きのために開いて
  はいけません。この問題を解決するために Unix のファイルロック機構を使
  うことができますが、この機構は Unix のバージョン間で異なり、使われて
  いるデータベースの実装について知識が必要となります。

class shelve.Shelf(dict, protocol=None, writeback=False, keyencoding='utf-8')

   "collections.abc.MutableMapping" のサブクラスで、 *dict* オブジェク
   ト内にpickle化された値を保持します。

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

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

   *keyencoding* パラメータは、shelf の背後にある dict に対して使われ
   る前にキーをエンコードするのに使用されるエンコーディングです。

   "Shelf" オブジェクトは、コンテキストマネージャとしても使用できます
   。この場合、 "with" ブロックが終了する際に、自動的に閉じられます。

   バージョン 3.2 で変更: *keyencoding* パラメータを追加; 以前はキーは
   常に UTF-8 でエンコードされていました。

   バージョン 3.4 で変更: コンテキストマネージャーサポートが追加されま
   した。

class shelve.BsdDbShelf(dict, protocol=None, writeback=False, keyencoding='utf-8')

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

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

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


12.3.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 = key in d            # true if the key exists
   klist = list(d.keys())     # a list of all existing keys (slow!)

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

   # 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

参考:

  "dbm" モジュール
     "dbm" スタイルのデータベースに対する共通インタフェース。

  "pickle" モジュール
     "shelve" によって使われるオブジェクト整列化機構。
