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

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

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

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

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

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

   デフォルトでは "pickle.DEFAULT_PROTOCOL" を使って作成した pickle で
   値を直列化します。 *protocol* 引数で pickle プロトコルのバージョン
   を指定できます。

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

   デフォルトでは "shelve" は直列化に "pickle.dumps()" を、非直列化に
   "pickle.loads()" を使います。 それぞれ *serializer* と
   *deserializer* を指定して変更できます。

   *serializer* 引数には、オブジェクト "obj" と *protocol* を受け取り
   、"obj" の表現を *bytes-like object* として返す呼び出し可能オブジェ
   クトを指定しなければなりません。 直列化する関数は *protocol* の値を
   無視しても構いません。

   *deserializer* 引数には、直列化されたオブジェクトを "bytes" オブジ
   ェクトとして受け取り、対応するオブジェクトを返す呼び出し可能オブジ
   ェクトを指定しなければなりません。

   *serializer* と *deserializer* の片方だけを指定すると "ShelveError"
   が送出されます。

   バージョン 3.10 で変更: デフォルトの pickle プロトコルに
   "pickle.DEFAULT_PROTOCOL" を使うようになりました。

   バージョン 3.11 で変更: ファイル名として *path-like object* を受け
   付けるようになりました。

   バージョン 3.15 で変更: "pickle.dumps()" と "pickle.loads()" の代わ
   りに独自の *serializer* 関数と *deserializer* 関数を指定できるよう
   になりました。

   注釈:

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

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

警告:

  "shelve" モジュールは "pickle" を使っているため、信頼できない提供元
  のシェルフを読み込むのは安全ではありません。 pickle と同様、シェルフ
  の読み込みによって任意のコードが実行される可能性があります。

シェルフオブジェクトは、辞書がサポートするメソッドと操作の大半に対応し
ています (コピー、コンストラクター、演算子 "|" と "|=" を除きます)。
そのため、辞書を使うスクリプトを永続ストレージが必要なスクリプトに容易
に移行できます。

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

Shelf.sync()

   *writeback* を "True" にしてシェルフを開いた場合、キャッシュ内のす
   べてのエントリーを書き戻します。 また、キャッシュを空にし、可能であ
   ればディスク上の永続辞書を同期します。 "reorganize()" を呼び出した
   ときや "close()" でシェルフを閉じたときに自動的に呼び出されます。

Shelf.reorganize()

   "sync()" を呼び出し、削除によって生じた空き領域を取り除いてディスク
   使用量を減らそうとします。

   Added in version 3.15.

Shelf.close()

   永続的な *辞書* オブジェクトを同期して閉じます。 閉じたシェルフに対
   する操作は "ValueError" で失敗します。

参考: 広くサポートされる保存形式を使い、組み込みの辞書と同じ速さで動作する
    永続辞書のレシピ。


制限事項
========

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

* "shelve" モジュールは、保存したオブジェクトへの *同時* 読み書きアク
  セスに対応していません (複数の読み込みアクセスを同時に行うのは安全で
  す)。 あるプログラムが書き込み用にシェルフを開いている間は、他のプロ
  グラムが読み込み用にも書き込み用にも開いてはいけません。 Unix のファ
  イルロックで対処できますが、Unix のバージョンによって異なり、使用す
  るデータベースの実装についての知識も必要です。

* macOS では "dbm.ndbm" が更新時にエラーを報告せずデータベースファイル
  を破損することがあり、その後の読み込みでプログラムがクラッシュするお
  それがあります。

* データベースパッケージによっては "Shelf.reorganize()" を利用できませ
  ん。 また、呼び出すとリソースの使用量 (特にディスク容量) が一時的に
  増える場合があります。 自動的に実行されることはないので、明示的に呼
  び出す必要があります。

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

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

   デフォルトでは "pickle.DEFAULT_PROTOCOL" を使って作成した pickle で
   値を直列化します。 *protocol* 引数で pickle プロトコルのバージョン
   を指定できます。 pickle プロトコルの説明は "pickle" のドキュメント
   を参照してください。

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

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

   *serializer* 引数と *deserializer* 引数は "open()" と同じように解釈
   されます。

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

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

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

   バージョン 3.10 で変更: デフォルトの pickle プロトコルに
   "pickle.DEFAULT_PROTOCOL" を使うようになりました。

   バージョン 3.15 で変更: *serializer* 引数と *deserializer* 引数が追
   加されました。

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

   A subclass of "Shelf" which exposes "first()", "next()",
   "previous()", "last()" and "set_location()" methods. These are
   available in the third-party "bsddb" module from pybsddb but not in
   other database modules.  The *dict* object passed to the
   constructor must support those methods.  This is generally
   accomplished by calling one of "bsddb.hashopen()", "bsddb.btopen()"
   or "bsddb.rnopen()".  The optional *protocol*, *writeback*,
   *keyencoding*, *serializer* and *deserializer* parameters have the
   same interpretation as in "open()".

   バージョン 3.15 で変更: *serializer* 引数と *deserializer* 引数が追
   加されました。

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

   辞書に似たオブジェクトの代わりに *filename* を受け取る "Shelf" のサ
   ブクラスです。 基盤となるファイルは "dbm.open()" で開きます。 デフ
   ォルトではファイルを作成し、読み書き両用で開きます。 省略可能な
   *flag* 引数は "open()" 関数と同じように解釈されます。 省略可能な
   *protocol*、*writeback*、*serializer*、*deserializer* 引数は
   "open()" と同じように解釈されます。

   バージョン 3.15 で変更: *serializer* 引数と *deserializer* 引数が追
   加されました。


使用例
======

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

   import shelve

   d = shelve.open(filename)  # 開く -- 下位ライブラリによってファイル名に
                              # 拡張子が付く場合がある

   d[key] = data              # key にデータを保存する (既存のキーなら
                              # 古いデータを上書きする)
   data = d[key]              # key のデータのコピーを取得する
                              # (キーがなければ KeyError を送出する)
   del d[key]                 # key に保存したデータを削除する
                              # (キーがなければ KeyError を送出する)

   flag = key in d            # キーが存在すれば真
   klist = list(d.keys())     # 既存のキーをすべて含むリスト (遅い！)

   # d は writeback=True を指定せずに開いたので注意:
   d['xx'] = [0, 1, 2]        # これは期待どおりに動作するが...
   d['xx'].append(3)          # これは動作しない！ d['xx'] は [0, 1, 2] のまま！

   # writeback=True を指定せずに d を開いた場合、慎重にコードを書く必要がある:
   temp = d['xx']             # コピーを取り出す
   temp.append(5)             # コピーを変更する
   d['xx'] = temp             # コピーを書き戻して変更を永続化する

   # d=shelve.open(filename,writeback=True) としておけば、単に
   # d['xx'].append(5) と書くだけで期待どおりに動作するが、
   # メモリ使用量が増え、d.close() の操作も遅くなる。

   d.close()                  # 閉じる


例外
====

exception shelve.ShelveError

   "open()"、"Shelf"、"BsdDbShelf"、"DbfilenameShelf" で
   *deserializer* と *serializer* の片方だけを指定したときに送出される
   例外です。

   *deserializer* 引数と *serializer* 引数は一緒に指定しなければなりま
   せん。

   Added in version 3.15.

参考:

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

  "pickle" モジュール
     "shelve" が使うオブジェクトの直列化。
