Edited at

pickle 2系で作ったものを3系で読み込むには

More than 3 years have passed since last update.


2系でpickle.dumpしたものを3系でpickle.loadしたい。

※文字の場合、byteのままなので、文字に直す方法を追記

※逆に、3系で作ったものを2系で読み込む方法を追記

いろいろ仕様が載ってるとこ:http://docs.python.jp/3.4/library/pickle.html

3系に移行して少し経って、前作ったpickleでdumpしたデータを読みたくなったが、エラーが出た。


エラー


pickle-load-error.

# test_w_2.pklは、2系で[1]をdumpしたファイル

In [25]: fin = open('test_w_2.pkl', 'r')
In [26]: pickle.load(fin)
TypeError: a bytes-like object is required, not 'str'

エラーがこのように出て、strではない?bytesにしないといけないっぽい


解決方法

openの時のオプションに'rb'をつければいいっぽい(バイナリーモード)


piclkle-load.

In [38]: fin = open('test_wb_2.pkl', 'rb')

In [39]: pickle.load(fin)
Out[39]: [1]


[追記]dumpしたのがstringの場合

上記の例では、[1]しかdumpしていませんでした。

リストの中に文字列を含んでいた場合にはloadできない場合があるようです。

pickle.loadする際に、encoding='bytes'でエンコード方法の変更。

受け取ったlistの中身のものを.decode('utf8')を用いてstringに変換する作業が必要です。


python2.7.9


pickle.dump

>>> pickle.dump(['あいうえお'], open('test.pkl', 'w')  )



python3.5.0


pickle.load

# 普通に読み込んでエラー

>>> pickle.load(open('test.pkl', 'r') )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not 'str'

# バイナリーで読み込んでも'ascii'に対応してないのでエラー
>>> pickle.load(open('test.pkl', 'rb') )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)

# エンコード方法を変えたら読み込めたが、bytesのままで読めない
>>> pickle.load(open('test.pkl', 'rb'), encoding='bytes' )
[b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a']

# decode('utf8')を使い、リスト内の各要素をdecode
>>> list(map(lambda x: x.decode('utf8'), pickle.load(open('test.pkl', 'rb'), encoding='bytes' ) ) )
['あいうえお']



[追記]逆に、3系で作ったものを2系で読み込むには

普通に3系で作ったもの(dump)だと2系では読み込め(load)ません

最初に書いたURLに書いてあるのですが、protocolを指定できるようです。

下記に各protocolバージョンの説明を載せときます



  • プロトコルバージョン 0 はオリジナルの「人間に判読可能な」プロトコルで、Python の初期のバージョンとの後方互換性を持ちます。

  • プロトコルバージョン 1 は旧形式のバイナリフォーマットで、これも Python の初期バージョンと互換性があります。

  • プロトコルバージョン 2 は Python 2.3 で導入されました。このバージョンでは 新方式のクラス のより効率的な pickle 化を提供しました。プロトコル 2 による改良に関する情報は PEP 307 を参照してください。

  • プロトコルバージョン 3 は Python 3.0 で追加されました。このバージョンで bytes オブジェクトをサポートしました。これは Python 2.x では非 pickle 化できません。これはデフォルトのプロトコルで、他の Python 3 バージョンとの互換性が求められる場合の推奨プロトコルです。

  • プロトコルバージョン 4 は Python 3.4 で追加されました。このバージョンでは巨大なオブジェクトのサポート、より多くの種類のオブジェクトの pickle 化、および一部のデータ形式の最適化が行われました。プロトコル 4 による改良に関する情報は PEP 3154 を参照してください。


python2系では、当然のごとくprotocolは2までしかないので、python3でもこれに合わせたほうがいいっぽい。(周りの人は、あまりpython2系をつかってないので)

見る限り、一番良さそうなのは4なので、自分だけが使う時にはこれ指定したほうが良さそうです。

ちなみに、デフォルトは3に設定されています。


python3.5


pickle.dump

>>> pickle.dump(['あいうえお'], open('test.pkl','wb'), protocol=2 ) 



python2.7


pickle.load

>>> pickle.load(open('test.pkl') ) 

[u'\u3042\u3044\u3046\u3048\u304a']
>>> print(pickle.load(open('test.pkl') )[0] )
あいうえお


まとめ

'rb'つければ、なんとかなりそう(いまのところ)

ほかにも、fix_importserrorsのオプションがあるけど、とりあえずこれで動く。

動かない場面が出てきたら追記します。