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 が使うオブジェクトの直列化。