超言理論

特に益もない日記である

Pythonのpickleでファイルに追記保存

旧題:めんどくさい物に蓋をする感じでファイルを追記したい*1
意訳すると「Pythonでちょっと複雑なデータを手軽に追記していきたい」ということ。
具体的には「pickleモジュールで適当にデータを保存して、そのdumpファイルに新しいデータをserialize & deserializeする」。


まず、pickleのおさらい。

pickle モジュールは Python オブジェクトの直列化および直列化されたオブジェクトの復元のためのバイナリプロトコルを実装しています。“Pickle 化” は Python オブジェクト階層をバイトストリームに変換する処理、“非 pickle 化” は (バイナリファイル または バイトらいくオブジェクト から) バイトストリームをオブジェクト階層に復元する処理を意味します。pickle 化 (および非 pickle 化) は “直列化 (serialization)”、”整列化 (marshalling)”、あるいは [1] “平坦化 (flattening)” とも呼ばれますが、混乱を避けるため、ここでは “Pickle 化”、”非 pickle 化” で統一します。

*2
頭悪く言うと、煩雑なデータ保存の手続きを超簡単にしてくれる便利なモジュール。

このモジュールは簡単に使えて、

import pickle
 
# test data
data = ['this','is','a','test','.']
 
if __name__ == '__main__':
  print('raw data:\n'+str(data))
  # serialize
  with open('pickle_test.dump', 'wb') as f:
    pickle.dump(data, f)
 
  # deserialize
  with open('pickle_test.dump', 'rb') as f:
    data = pickle.load(f)  

ファイルオブジェクトとデータを一緒に渡してdumpすることでデータを保存(serialize)、dumpしたデータをloadすることで復元読み出し(deserialize)することができる。
このpickleの便利な点は複雑なデータをserializeしても、deserializeするときは型を指定したりしなくても自動で復元してくれるというところ。
ということで、保存するときにどう書き出すか悩むような煩雑な構造をしているデータをまるっと保存するときに非常に便利である。*3


で、これが便利なので多用したいわけだが、一つ問題として、どうやら追記に対応していないようである。
試しにこういうコードを書いてみた。

import pickle

# test data
data = ['this','is','a','test','.']
appendix = ['this','is','appendix','.']

print('raw data:\n'+str(data))
# serialize
with open('pickle_test.dump', 'wb') as f:
  pickle.dump(data, f)

# deserialize
with open('pickle_test.dump', 'rb') as f:
  data = pickle.load(f)

print('pickle data:\n'+str(data))

# try append
with open('pickle_test.dump', 'ab') as f:
  pickle.dump(appendix, f)

with open('pickle_test.dump', 'rb') as f:
  data = pickle.load(f)

print('appended pickle data:\n'+str(data))

実行結果はこんな感じになる。

python3 pickle_append.py
raw data:
['this', 'is', 'a', 'test', '.']
pickle data:
['this', 'is', 'a', 'test', '.']
appended pickle data:
['this', 'is', 'a', 'test', '.']

最初にserializeしたデータしかdeserializeされなかった。
何とかならないかなと思って調べたら似たようなことをやっている人がすでにいた。

How to use append with pickle in python? - Stack Overflow

ので参考にさせてもらって以下のようなコードを用意した。

import pickle


# test data
data = ['this','is','a','test','.']
appendix = ['this','is','appendix','.']


def load_dumps(f):
  obj = []
  while 1:
    try:
      obj.extend(pickle.load(f))
    except:
      break
  return obj

print('raw data:\n'+str(data))
# serialize
with open('pickle_test.dump', 'wb') as f:
  pickle.dump(data, f)

# deserialize
with open('pickle_test.dump', 'rb') as f:
  data = pickle.load(f)

print('pickle data:\n'+str(data))

# try append
with open('pickle_test.dump', 'ab') as f:
  pickle.dump(appendix, f)

with open('pickle_test.dump', 'rb') as f:
  data = load_dumps(f)

print('appended pickle data:\n'+str(data))

実行結果はこんな感じ。

python pickle_aw.py
raw data:
['this', 'is', 'a', 'test', '.']
pickle data:
['this', 'is', 'a', 'test', '.']
appended pickle data:
['this', 'is', 'a', 'test', '.', 'this', 'is', 'appendix', '.']

からくりは簡単で、dumpした数だけ後ろに追記的にserializeされているので、追記されている分だけdeserializeしてextendなりappendなりしてデータをさらに復元(結合)してやればいい。



これでTwitterのクローラで思考停止データ保存が捗る。
created_atとかscreen_nameが利用したくなったのに、クローラがtextとstatus_idしか保存してないなんてことはなくなる。

*1:あまりにも分かりにくいという感じだったので、タイトルを修正した。

*2:引用:12.1. pickle — Python オブジェクトの直列化 — Python 3.3.6 ドキュメント

*3:学習したNNモデルとか、Twitterのstatusオブジェクトとか


Copyright © 2012-2016 Masahiro MIZUKAMI All Rights Reserved.