連載
» 2021年06月08日 05時00分 公開

[解決!Python]バイナリファイルを読み書きするには:shelve編解決!Python

shelveモジュールを使って、辞書と同じ使い勝手で外部ファイルにオブジェクトを永続化したり、そこからオブジェクトを復元したりする方法を紹介する。

[かわさきしんじ,Deep Insider編集部]

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

「解決!Python」のインデックス

連載目次

import shelve

data_file = 'mydata'
key = 'person_data'

person_data = [('kawasaki', 120), ('isshiki', 38)]

with shelve.open(data_file) as d:
    d[key] = person_data

with shelve.open(data_file) as d:
    data = d[key]

print(data)  # [('kawasaki', 120), ('isshiki', 38)]

# デフォルトでは読み書き両用でオープンされる
num_data = [1, 2, 3, 4, 5]

with shelve.open(data_file) as d:
    data = d[key]  # 読み込み
    print(data)  # [('kawasaki', 120), ('isshiki', 38)]
    d['num_data'] = num_data  # 書き込み
    nums = d['num_data'# 読み込み
    print(nums)  # [1, 2, 3, 4, 5]

# writeback=False
more_data = ('endo', 45)

with shelve.open(data_file) as d:
    data = d[key]
    data.append(more_data)  # 読み出したデータに追加
    print(d[key])  # [('kawasaki', 120), ('isshiki', 38)]
    d[key] = data  # 反映するには元のキーの値を置き換える必要がある
    print(d[key])  # [('kawasaki', 120), ('isshiki', 38), ('endo', 45)]

# writeback=True
one_more_data = ('shimada', 50)

with shelve.open(data_file, writeback=True) as d:
    data = d[key]
    data.append(one_more_data)  # Shelfオブジェクトへの操作はキャッシュされる
    print(d[key])  # [('kawasaki', 120), ('isshiki', 38), ('endo', 45), ('shimada', 50)]
    # closeメソッドかsyncメソッドの呼び出しで、キャッシュの内容が書き込まれる。
    # キャッシュサイズが大きくなると書き戻しに時間がかかる点には注意


shelveモジュールとは

 Pythonに標準で添付されているshelveモジュールを利用すると、Pythonの辞書と似た形式でオブジェクトを永続化できる。つまり「shelveオブジェクト[キー] = 値」のようにして「値」に指定したオブジェクトを外部ファイルに永続化したり、逆に「値 = shelveオブジェクト[キー]」として外部ファイルからオブジェクトを取り出したりできる(「shelve」は「棚に何かを置く」といった意味)。

 このとき、「キー」には文字列を指定する。「値」にはpickle化可能なオブジェクトなら何でも指定できる(内部ではpickleモジュールが使われているので、pickleモジュールと同様な安全でない操作が可能な点には注意)。

 shelveモジュールを使って、永続化を行う基本的な方法はそのopen関数を呼び出して、Shelfクラス(またはそのサブクラス)のインスタンスを取得して、それを用いて辞書的なアクセスを行うことだ。以下にshelve.open関数の構文を示す。

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


 filenameには内部で使用するデータベースファイルを指定する(多くの場合は、filenameに指定した文字列に何らかの拡張子が付いたものが実際のファイル名になるだろう)。flagにはファイルをオープンするモードを指定する。指定できるのは以下の値。

  • 'r':読み込み専用
  • 'w':書き込み専用
  • 'c':読み書き両用
  • 'n':新規作成

 指定を省略した場合には'c'が指定されたものとして扱われ、ファイルは読み書き両用でオープンされる。protocolは内部で使用するpickleのプロトコルバージョンである。writebackをTrueにすると、open関数で取得したShelfオブジェクトに格納されているエントリ(キーと値の組)に対する操作がキャッシュされるようになる。キャッシュの内容は、closeメソッドかsyncメソッドを呼び出したときに外部ファイルへ書き込まれる。デフォルト値はFalseであり、キャッシュは行われない。それぞれの動作の違いについては後述)。

 open関数により取得したShelfオブジェクトには辞書的なアクセスを行い、オブジェクトの永続化や復元を行い、Shelfオブジェクトの使用が終わったらcloseメソッドを呼び出す。

# shelveモジュールの使い方
import shelve

d = shelve.open('somefile'# shelve.open関数でShelfオブジェクトを取得
d[somekey] = somevalue  # 書き込み(永続化)
somevalue = d[somekey]  # 復元
d.close()  # 使い終わったらcloseメソッドを呼び出す


 shelve.open関数はwith文と組み合わせて次のようにも書ける。

# with文と組み合わせる
import shelve

with shelve.open('somefile') as d:
    d[somekey] = somevalue
    somevalue = d[somekey]


 これにより、closeメソッドを明示的に呼び出す必要がなくなるので、以下ではこちらの書き方でサンプルを示す。

shelveモジュールを使ったオブジェクトの永続化と復元

 以下にshelveモジュールを使ってオブジェクトの永続化と復元を行う例を示す。ここでは永続化先のファイル名は「mydata」(mydata.db)として、永続化/復元を行うデータはタプルを要素とするリストにしてある。これを'person_data'というキーを使って永続化/復元する。

import shelve

data_file = 'mydata'
key = 'person_data'

person_data = [('kawasaki', 120), ('isshiki', 38)]

with shelve.open(data_file) as d:
    d[key] = person_data  # 永続化

with shelve.open(data_file) as d:
    data = d[key]  # 復元

print(data)  # [('kawasaki', 120), ('isshiki', 38)]


 この例では、永続化(書き込み)と復元(読み込み)を別々のwith文で行っているが、先ほども述べた通り、デフォルトではshelve.open関数は読み書き両用でファイルをオープンする。そのため、実際には次のようなコードも記述できる。

num_data = [1, 2, 3, 4, 5]

with shelve.open(data_file) as d:
    data = d[key]  # 読み込み
    print(data)  # [('kawasaki', 120), ('isshiki', 38)]
    d['num_data'] = num_data  # 書き込み
    nums = d['num_data'# 読み込み
    print(nums)  # [1, 2, 3, 4, 5]


writebackパラメーターの値による動作の違い

 shelve.open関数のwritebackパラメーターの値をTrueにすると、Shelfオブジェクトに格納されているエントリの操作がキャッシュされる。一方、Falseにすると、キャッシュはされず、Shelfオブジェクトを介した読み込み/書き込みは外部ファイルにすぐに反映される(デフォルト)。

Copyright© Digital Advantage Corp. All Rights Reserved.

編集部からのお知らせ

6月16日にフォーマット統一のため利用規約を変更します

RSSについて

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

メールマガジン登録

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