shelveモジュールを使うと、辞書と似た使い勝手でPythonオブジェクトを外部ファイルに保存できる。その使い方と注意点についてまとめる。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
前回は、pickleモジュールによるオブジェクトの直列化について見た。今回は、辞書のような使い勝手でオブジェクトをファイルへ保存できるshelveモジュールを紹介する。
shelveモジュールは名前/値の組という形で、Pythonのオブジェクトをファイルなどに保存するために使える。内部では前回に紹介したpickleモジュールを使用しているので、実際に保存できるものはpickleモジュールと同等だ。
shelveモジュールにはopen関数があり、これを使うことで新しくShelfオブジェクトが作成される*1。
*1 実際には、shelveモジュールで定義されているDbfilenameShelfクラスのインスタンスが生成される。このDbfilenameShelfクラスはShelfクラスから派生していることから、ここではShelfオブジェクトと表現している。なお「shelf」は「棚」という意味で、「shelve」は「棚に何かを置く」「ものごとを棚上げ(延期)する」といった意味だ。つまり、「shelve」モジュールとは、「棚に何かを置いて、それを取っておく」機能を提供するモジュールだと考えられる(shelfの複数形は「shelves」)
以下にopen関数の基本構文を示す。
open(filename, protocol=None, writeback=False)
Shelfオブジェクトをオープンする。filenameには内部で使用するデータベースファイルのファイル名を指定する。protocolには内部で使用するpickleのプロトコルバージョンを指定する(省略時にはデフォルトのプロトコルが使用される)。writebackはデータベースの更新方法を制御する。
パラメーター | 説明 |
---|---|
filename | 内部で使用するデータベースファイルのファイル名。そのファイル名にさらに拡張子が付加されることがある |
protocol | 内部で使用するpickleのプロトコルバージョン。省略時にはデフォルトのプロトコルが使用される |
writeback | データベースの更新方法を制御する。省略時にはFalseが指定されたものと見なされる |
shelve.open関数のパラメーター |
open関数はShelfオブジェクト(DbfilenameShelfオブジェクト)を戻り値とするので、これを受け取れば、それを辞書と似た使い勝手で、Pythonのオブジェクトをそれに保存できる。
例えば、アドレス帳のようなものを考えてみよう。以下に、人の名前をキーとして、その人の住所と電話番号をShelfオブジェクトに保存する例を示す。
import shelve
myaddrbook = shelve.open('myaddrbook')
myaddrbook['kawasaki'] = {'addr': 'Setagaya', 'phone': '1234'}
myaddrbook['isshiki'] = {'addr': 'Suginami', 'phone': '4567'}
print(myaddrbook['kawasaki'])
myaddrbook.close()
オープンしたShelfオブジェクトを使い終わったら、closeメソッドで閉じるのは通常のファイルと同様だ。これにより、Shelfオブジェクトと実際のデータベースファイルとの同期が取られて、Shelfオブジェクトの内容が外部の(データベース)ファイルに保存されることが保証される。
実行結果を以下に示す。
なお、Shelfオブジェクトのキーとして利用できるのは、文字列だけとなっている。以下に例を示す。
myaddrbook = shelve.open('myaddrbook')
myaddrbook[0] = {'addr': 'Itabashi', 'phone': '6789'}
これを実行すると次のようにエラーとなる。
通常の辞書では「ハッシュ可能なオブジェクト」をキーとして利用できたが(文字列や数値、変更可能な要素を含まないタプルなど)、Shelfオブジェクトではそうではないことに注意しよう。
次にshelveモジュールを利用する際の注意点について見るが、その前に「myaddrbook.close()」を実行していったん閉じておこう。
Pythonのドキュメント「shelve --- Python オブジェクトの永続化」には「シェルフには永続的な辞書の可変エントリがいつ変更されたかを知る術がありません。 デフォルトでは、変更されたオブジェクトはシェルフに代入されたとき だけ 書き込まれます」とある。つまり、Shelfオブジェクトは辞書のように使えるが、その振る舞いがビックリするような結果となることもあるということだ。
以下に例を示す。
myaddrbook = shelve.open('myaddrbook')
mydict = {'kawasaki': {'addr': 'Setagaya', 'phone': '1234'}} # 同じデータの辞書
myaddrbook['kawasaki']['addr'] = 'Shibuya'
mydict['kawasaki']['addr'] = 'Shibuya'
print(myaddrbook['kawasaki']['addr'])
print(mydict['kawasaki']['addr'])
myaddrbook.close()
このコードでは、上で作成したアドレス帳のデータ(の1エントリ)と同じデータを持つ辞書を作成して、Shelfオブジェクトと辞書オブジェクトでその住所を書き換えている。実際に上のコードを実行すると、どうなるだろう。
このように、辞書については書き換えた結果が得られるが、Shelfオブジェクトではそうなっていない。これが上で述べた「デフォルトでは、変更されたオブジェクトはシェルフに代入されたとき だけ 書き込まれます」という振る舞いによるものだ。ちなみに上のコードでは「myaddrbook.close()」を実行しても、更新したつもりのデータは外部ファイルに反映されない。
こうした振る舞いにビックリしないようにするには、2つの方法がある。1つはデータの更新にはShelfオブジェクトへの代入を常に行うようにすること。もう1つは、Shelfオブジェクトのオープン時にパラメーターwritebackにTrueを指定することだ。順番に見ていこう。
まず、Shelfオブジェクトに代入することで、データを更新する方法だ。これは次のようになる。
myaddrbook = shelve.open('myaddrbook')
data = myaddrbook['kawasaki']
data['addr'] = 'Shibuya'
myaddrbook['kawasaki'] = data
print(myaddrbook['kawasaki']['addr'])
myaddrbook.close()
この方法では、一時変数にShelfオブジェクトからデータを取り出して、その値を変更した後に、Shelfオブジェクトの該当エントリに代入し直す。これを実行すると、次のように変更したデータがきちんと反映される。
もう1つの方法は、Shelfオブジェクトのオープン時にパラメーターwritebackにTrueを渡すだけだ。この場合、Shelfオブジェクトを介して、外部ファイルに対して読み書きが行われたデータは全てコンピュータのメモリにキャッシュされるようになる。キャッシュされたデータに対する読み書きは辞書と同様に振る舞い、Shelfオブジェクトをクローズした時点で外部ファイルへと書き出される。
Copyright© Digital Advantage Corp. All Rights Reserved.