ポインタをもつクラスをPickle化しようとしたらエラーを吐いたので、その対処法のメモ。
__getstate__メソッドの実装
ポインタにしているオブジェクトを復元できるだけの情報を一旦Pythonオブジェクトのメンバにして、__dict__
をstate
として返す。
__setstate__メソッドの実装
__getstate__
で吐き出した情報からポインタオブジェクトを再作成。
実装例
cdef class SomeClass: cdef CppClass* cpp def __init__(self, id: int) -> None: # イニシャライザ self.cpp = new CppClass(id) def __dealloc__(self) -> None: # new したメモリの解放 del self.cpp def __getstate__(self): # pickle化する時に呼び出される self.id = self.cpp.getid() # 復元できるだけの情報を取得 state = self.__dict__.copy() del self.id # 不要なオブジェクトを削除 return state def __setstate__(self, state): # unpickle化する時に呼び出される self.__dict__.update(state) self.cpp = new CppClass(self.id) # 再作成。必要があればセッターを使って復元する del self.id
その他
より低レベルなPickleの実装として、__reduce__
があるけど、おそらく可読性や実装の楽さで__getstate__, __setstate__
が勝る。
というより、__reduce__
でリビルダーを上手く実装できなかった。
参考資料
pickle --- Python オブジェクトの直列化 — Python 3.9.1 ドキュメントより引用
object.__getstate__()
クラスはそのインスタンスをどう pickle 化するかについてさらに影響を与えることができます; クラスに __getstate__() メソッドが定義されていた場合それが呼ばれ、返り値のオブジェクトはインスタンスの辞書ではなく、インスタンスの内容が pickle 化されたものになります。__getstate__() がないときは通常通りインスタンスの __dict__ が pickle 化されます。object.__setstate__(state)
非 pickle 化に際して、クラスが __setstate__() を定義している場合、それは非 pickle 化された状態とともに呼び出されます。その場合、状態オブジェクトが辞書でなければならないという要求はありません。そうでなければ、 pickle された状態は辞書で、その要素は新しいインスタンスの辞書に割り当てられます。__getstate__() および __setstate__() メソッドの使い方に関する詳細な情報については 状態を持つオブジェクトの扱い 節を参照してください。