pythonのdillでは正規表現matchオブジェクトが保存できない

dillというのは、pickleの強化版のようなツールだ。現在使っている変数をまとめて外部のファイルに保存できる。
(バイナリにして保存することを、pickle化、シリアライズ、直列化などと呼ぶらしい)

dillについての情報はネットにあまり書いていない。下記の記事がとても参考になった。
Pythonですべての変数を保存するにはPickleよりdillが便利 - Qiita

余談ですが、「dill pickle」はピクルスの一種を指しているらしく、この語句で検索してもpythonのライブラリは出てきません。検索ワードに「python」を加えましょう。

dill pickle
《料理》ディル・ピクルス◆キュウリをディル(dill)の葉と種と共に塩漬けした物。
dill pickleの意味・用例|英辞郎 on the WEB:アルク

簡単な使い方

公式ドキュメントに使い方が載っていないので、ほぼ上記Qiitaそのままですが。

# dill_test_a.py
a = 123
b = 456
c = b - a
string = "Hello World"

import dill
dill.dump_session("session.pkl")

最後の2行で、現在の変数をすべて"session.pkl"というファイルに保存している。

# dill_test_b.py
import dill
dill.load_session("session.pkl")

print(a)
print(b)
print(c)
print(string)

'''出力:
123
456
333
Hello World
'''

保存した"session.pkl"から変数を復元し、その値を表示している。

pickleでは保存できないが、dillで保存できる例

上の例では単純な数値や文字列だけを保存(シリアライズ、直列化)した。
しかし、pythonには色々なオブジェクトがあり、pickleでは保存できないものもある。
今回の主題ではないし、私がきちんと理解していないので詳細は省くが、lambda関数、入れ子になった関数などがこれに該当する。dillではpickleを拡張子、lambda関数、入れ子になった関数なども保存できる。

公式の説明では、
12.1. pickle ? Python オブジェクトの直列化 - Python 3.6.5 ドキュメント の中の『12.1.4. pickle 化、非 pickle 化できるもの』
dillのreadme
を参照。

pickleオブジェクトがpickle化できるものの中で比較的、制限されているからです。つまり入れ子関数やlambda、スライスや、その他いろいろなものを処理できません。これらのオブジェクトを直接pickle化したいケースは、それほどはないかもしれませんが、pickle化したい他の物の中に、そういったオブジェクトが出てくることは、かなり一般的です。そのため、pickle化が失敗する原因となるのです。
私が選ぶ2015年の”新しい”Pythonモジュール トップ5 | POSTD

一部、pickle化できない種類のオブジェクトがある。
よく引っかかる例はlambda関数や、openで開いたファイルハンドラで、これが保存するオブジェクトのどこか1箇所にでも使われていると、pickle.dump()がエラーを出す。
pickle [いかたこのたこつぼ]

正規表現のマッチングオブジェクトが入っているとエラーになる

本題に入る。
色々なオブジェクトを保存できるdillだが、正規表現のmatchオブジェクトは保存できない。

import re
result = re.search("lar", "regular expression")
print(result)
print(type(result))

'''出力:
<_sre.SRE_Match object; span=(4, 7), match='lar'>
<class '_sre.SRE_Match'>
'''

"result"というmatchオブジェクトを作った。ではこれをdillで保存してみよう。

dill.dump_session("session2.pkl")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-07b38aa0950a> in <module>()
----> 1 dill.dump_session("session2.pkl")

D:\Code\Anaconda3\lib\site-packages\dill\dill.py in dump_session(filename, main, byref)
    331         pickler._recurse = False # disable pickling recursion for globals
    332         pickler._session = True  # is best indicator of when pickling a session
--> 333         pickler.dump(main)
    334     finally:
    335         f.close()

(長いので中略)

D:\Code\Anaconda3\lib\pickle.py in save(self, obj, save_persistent_id)
    494             reduce = getattr(obj, "__reduce_ex__", None)
    495             if reduce is not None:
--> 496                 rv = reduce(self.proto)
    497             else:
    498                 reduce = getattr(obj, "__reduce__", None)

TypeError: can't pickle _sre.SRE_Match objects

ダメだ。matchオブジェクトは保存できない。


解決方法

今回は、「matchオブジェクトも保存する」のではなく、「matchオブジェクトは保存できなくても良いから、その他のオブジェクトをまとめて保存する」ことを目指す。
つまり解決と言っているのは、具体的には「matchオブジェクト以外を保存する」ことである。

dump_session()関数に「指定した変数は除外する」という引数でも無いかなぁと思ったが、
公式ドキュメントを見てもそういう引数はない。
dill module documentation - dill 0.2.9.dev0 documentation

したがって、変数を削除してからdillを実行することにする。
変数の削除はdel [変数名]で実行できる。

import re
result = re.search("lar", "regular expression")
print(result)
print(type(result))

'''出力:
<_sre.SRE_Match object; span=(4, 7), match='lar'>
<class '_sre.SRE_Match'>
'''

del result
dill.dump_session("session2.pkl")

でresult以外の変数を保存できた。
保存したファイルを使用して変数を表示することもできた。(ソースコードは最初の例のファイル名を変えただけなので特に貼らない)

それでは。