#2系でpickle.dumpしたものを3系でpickle.loadしたい。
※文字の場合、byteのままなので、文字に直す方法を追記
※逆に、3系で作ったものを2系で読み込む方法を追記
いろいろ仕様が載ってるとこ:http://docs.python.jp/3.4/library/pickle.html
3系に移行して少し経って、前作ったpickle
でdumpしたデータを読みたくなったが、エラーが出た。
#エラー
# 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'をつければいいっぽい(バイナリーモード)
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(['あいうえお'], open('test.pkl', 'w') )
###python3.5.0
# 普通に読み込んでエラー
>>> 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(['あいうえお'], open('test.pkl','wb'), protocol=2 )
##python2.7
>>> pickle.load(open('test.pkl') )
[u'\u3042\u3044\u3046\u3048\u304a']
>>> print(pickle.load(open('test.pkl') )[0] )
あいうえお
#まとめ
'rb'つければ、なんとかなりそう(いまのところ)
ほかにも、fix_imports
とerrors
のオプションがあるけど、とりあえずこれで動く。
動かない場面が出てきたら追記します。