shelve – 任意の Python オブジェクトの永続ストレージ

目的:shelve モジュールは、ディクショナリのような API で pickle 化できる任意の Python オブジェクトの永続ストレージを実装する

shelve モジュールは、関係データベースだと行き過ぎなときに Python オブジェクトの簡単な永続ストレージの選択肢として使用できます。シェルフはちょうどディクショナリのようにキーでアクセスできます。その値は pickle 化されて anydbm が作成、管理するデータベースへ書き込まれます。

新たなシェルフを作成する

shelve を使用する最も簡単な方法は DbfilenameShelf を経由することです。それはデータを格納するために anydbm を使用します。そのクラスを直接使用するか、単純に shelve.open() を呼び出します。

import shelve

s = shelve.open('test_shelf.db')
try:
    s['key1'] = { 'int': 10, 'float':9.5, 'string':'Sample data' }
finally:
    s.close()

再度データへアクセスするには、シェルフをオープンしてディクショナリのように使用します。

import shelve

s = shelve.open('test_shelf.db')
try:
    existing = s['key1']
finally:
    s.close()

print existing

両方のサンプルスクリプトを実行すると次のようになります。

$ python shelve_create.py
$ python shelve_existing.py
{'int': 10, 'float': 9.5, 'string': 'Sample data'}

dbm モジュールは、複数のアプリケーションから同時に同じデータベースへ書き込むのをサポートしません。クライアントアプリケーションがシェルフを変更しないなら、そのデータベースを読み込み専用でオープンするようにシェルフへ伝えることができます。

import shelve

s = shelve.open('test_shelf.db', flag='r')
try:
    existing = s['key1']
finally:
    s.close()

print existing

読み込み専用でオープンされたデータベースを変更しようとする場合、アクセスエラー例外が発生します。その例外の種類は、データベースの作成時に anydbm が選択したデータベースモジュールで変わります。

ライトバック(Write-back)

シェルフはデフォルトで volatile オブジェクトの変更を保存しません。シェルフに格納された要素のコンテンツを変更するなら、再度その要素を格納して明示的にそのシェルフを更新しなければなりません。

import shelve

s = shelve.open('test_shelf.db')
try:
    print s['key1']
    s['key1']['new_value'] = 'this was not here before'
finally:
    s.close()

s = shelve.open('test_shelf.db', writeback=True)
try:
    print s['key1']
finally:
    s.close()

このサンプルでは、’key1’ のディクショナリは再度、格納されていません。そのため、シェルフを再オープンするとその変更が保存されていません。

$ python shelve_create.py
$ python shelve_withoutwriteback.py
{'int': 10, 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'float': 9.5, 'string': 'Sample data'}

シェルフに格納された volatile オブジェクトに対する変更を自動的に捕捉するには、ライトバック(writeback)を有効にしてシェルフをオープンします。ライトバックフラグは、インメモリキャッシュでデータベースから取り出した全てのオブジェクトをシェルフに覚えさせます。また、それぞれのキャッシュオブジェクトは、シェルフがクローズされるときにデータベースへ書き込まれます。

import shelve

s = shelve.open('test_shelf.db', writeback=True)
try:
    print s['key1']
    s['key1']['new_value'] = 'this was not here before'
    print s['key1']
finally:
    s.close()

s = shelve.open('test_shelf.db', writeback=True)
try:
    print s['key1']
finally:
    s.close()

これはプログラマによる人為的なエラーを減少させて、より透過的にオブジェクトを永続化させますが、ライトバックモードを使用することが全ての状況で望まれるわけではないかもしれません。キャッシュは、シェルフがオープンされている間、余分なメモリを消費します。そして、クローズされるときにキャッシュされたオブジェクトをデータベースへ書き込むために余分な時間がかかります。キャッシュされたオブジェクトが変更されたかを伝える方法がないので、そのキャッシュは全て書き込まれます。アプリケーションのデータが書き込みよりも読み込みが多い場合、ライトバックは想像以上にオーバーヘッドになるかもしれません。

$ python shelve_create.py
$ python shelve_writeback.py
{'int': 10, 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'new_value': 'this was not here before', 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'new_value': 'this was not here before', 'float': 9.5, 'string': 'Sample data'}

特別なシェルフ

前節で紹介した全てのサンプルはデフォルトのシェルフの実装を使用します。シェルフ実装のデフォルトではなく shelve.open() を使用するのは一般的な使用方法です。特にデータを格納するために使用されるデータベースを気にしない場合です。そうは言っても、気にするときもあります。そういった状況では DbfilenameShelfBsdDbShelf を直接的に使用したり、もしくはカスタム目的で Shelf をサブクラス化した方が良いかもしれません。

See also

shelve
本モジュールの標準ライブラリドキュメント
anydbm
anydbm モジュール
feedcache
デフォルトストレージオプションとして shelve を使用する feedcache モジュール
shove
バックエンドフォーマットでよく似た API を実装する shove モジュール
データの永続化と変換
標準モジュールでデータを格納するその他の仕組み
Bookmark and Share