[Python入門]shelveモジュールによるオブジェクトの永続化Python入門

shelveモジュールを使うと、辞書と似た使い勝手でPythonオブジェクトを外部ファイルに保存できる。その使い方と注意点についてまとめる。

» 2019年10月08日 05時00分 公開
[かわさきしんじDeep Insider編集部]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「Python入門」のインデックス

連載目次

 前回は、pickleモジュールによるオブジェクトの直列化について見た。今回は、辞書のような使い勝手でオブジェクトをファイルへ保存できるshelveモジュールを紹介する。

shelveモジュール

 shelveモジュールは名前/値の組という形で、Pythonのオブジェクトをファイルなどに保存するために使える。内部では前回に紹介したpickleモジュールを使用しているので、実際に保存できるものはpickleモジュールと同等だ。

 shelveモジュールにはopen関数があり、これを使うことで新しくShelfオブジェクトが作成される*1

*1 実際には、shelveモジュールで定義されているDbfilenameShelfクラスのインスタンスが生成される。このDbfilenameShelfクラスはShelfクラスから派生していることから、ここではShelfオブジェクトと表現している。なお「shelf」は「棚」という意味で、「shelve」は「棚に何かを置く」「ものごとを棚上げ(延期)する」といった意味だ。つまり、「shelve」モジュールとは、「棚に何かを置いて、それを取っておく」機能を提供するモジュールだと考えられる(shelfの複数形は「shelves」)


 以下にopen関数の基本構文を示す。

shelve.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()

shelveモジュールの使用例

 オープンしたShelfオブジェクトを使い終わったら、closeメソッドで閉じるのは通常のファイルと同様だ。これにより、Shelfオブジェクトと実際のデータベースファイルとの同期が取られて、Shelfオブジェクトの内容が外部の(データベース)ファイルに保存されることが保証される。

 実行結果を以下に示す。

実行結果 実行結果

 なお、Shelfオブジェクトのキーとして利用できるのは、文字列だけとなっている。以下に例を示す。

myaddrbook = shelve.open('myaddrbook')

myaddrbook[0] = {'addr': 'Itabashi', 'phone': '6789'}

Shelfオブジェクトのキーに文字列以外のオブジェクトは使えない

 これを実行すると次のようにエラーとなる。

実行結果 実行結果

 通常の辞書では「ハッシュ可能なオブジェクト」をキーとして利用できたが(文字列や数値、変更可能な要素を含まないタプルなど)、Shelfオブジェクトではそうではないことに注意しよう。

 次にshelveモジュールを利用する際の注意点について見るが、その前に「myaddrbook.close()」を実行していったん閉じておこう。

shelveモジュールを利用する際の注意点

 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オブジェクトからデータを取り出して、その値を変更した後に、Shelfオブジェクトの該当エントリに代入し直す。これを実行すると、次のように変更したデータがきちんと反映される。

実行結果 実行結果

 もう1つの方法は、Shelfオブジェクトのオープン時にパラメーターwritebackにTrueを渡すだけだ。この場合、Shelfオブジェクトを介して、外部ファイルに対して読み書きが行われたデータは全てコンピュータのメモリにキャッシュされるようになる。キャッシュされたデータに対する読み書きは辞書と同様に振る舞い、Shelfオブジェクトをクローズした時点で外部ファイルへと書き出される。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。