Pythonのpickleでserializeできない問題の回避策(dill)
読み込んだデータをシリアライズしておきたい場合、pickleを使えば任意のクラスのオブジェクトを扱える。ファイルを開いて、dump
で書き出し、load
で読み出しを行えば良い。しかし、ある構造を持つクラスのオブジェクトをpickleでシリアライズしようとすると、以下のようにエラーとなる。
$ python --version Python 3.4.3 $ python pickle_test.py Traceback (most recent call last): File "pickle_test.py", line 15, in <module> pickle.dump(klass,f) _pickle.PicklingError: Can't pickle <function Klass.__init__.<locals>.<lambda> at 0x107728ae8>: attribute lookup <lambda> on __main__ failed
クラスの内容は以下の通りで、defaultdict
をネストしているので、lambda
を与えている。
$ cat pickle_test.py # coding: utf-8 import pickle from collections import defaultdict class Klass: def __init__(self): self.prop = defaultdict(lambda:defaultdict(int)) klass = Klass() klass.prop["x"]["y"] += 10 with open("test.pickle","wb") as f: pickle.dump(klass,f) with open("test.pickle","rb") as f: newklass = pickle.load(f) print(newklass.prop["x"]["y"])
以下をみると、同じようなエラーがlambda
を使った際に発生するようだ。
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed - あと5分だけ
そこで、defaultdict
の引数にlambda
ではなく関数を渡すようにするとうまくいく。一応、defaultdict
のネストにも対応できる。もう少し賢いやり方があるかもしれない。
$ cat pickle_test2.py # coding: utf-8 import pickle from collections import defaultdict class Val: def __init__(self): self.vprop= defaultdict(int) def ddv(): return defaultdict(Val) def dd2(): return defaultdict(dd) def dd(): return defaultdict(int) class Klass: def __init__(self): self.prop = defaultdict(dd) self.prop2= defaultdict(dd2) self.prop3= defaultdict(ddv) klass = Klass() klass.prop["x"]["y"] += 10 klass.prop2["x"]["y"]["z"] += 100 klass.prop3["x"]["y"].vprop["z"] += 1000 with open("test.pickle","wb") as f: pickle.dump(klass,f) with open("test.pickle","rb") as f: newklass = pickle.load(f) print(newklass.prop["x"]["y"]) print(newklass.prop2["x"]["y"]["z"]) print(newklass.prop3["x"]["y"].vprop["z"]) $ python pickle_test2.py 10 100 1000
追記 dill
コメントで教えていただきましたdillを使えば良いことが分かりました。もうpickle必要無さそうですね。dillはpip
でインストールします。
$ cat dill_test.py # coding: utf-8 import dill from collections import defaultdict class Val: def __init__(self): self.vprop= defaultdict(int) class Klass: def __init__(self): self.prop = defaultdict(lambda:defaultdict(int)) self.prop2= defaultdict(lambda:defaultdict(lambda:defaultdict(int))) self.prop3= defaultdict(lambda:defaultdict(Val)) klass = Klass() klass.prop["x"]["y"] += 10 klass.prop2["x"]["y"]["z"] += 100 klass.prop3["x"]["y"].vprop["z"] += 1000 with open("test.pickle","wb") as f: dill.dump(klass,f) with open("test.pickle","rb") as f: newklass = dill.load(f) print(newklass.prop["x"]["y"]) print(newklass.prop2["x"]["y"]["z"]) print(newklass.prop3["x"]["y"].vprop["z"])
$ pip install dill ... $ python dill_test.py 10 100 1000