hatunina’s blog

メモと日記です

Python2.7からPython3.5へ移行した

まだ完全に移行完了した訳ではないけど覚えているうちにまとめておく

理由

システム自体はPython2.7で動いているんだけど、新しくNeuroNERというライブラリを使うことになり、これ3.x系じゃないと動かないやんけ!ってなったからです。

やったこと

ざっくりまとめるとこんなことやりました。

① デフォルトで組み込まれている2to3で大部分を自動で3.x系のコードへ変換
② 変換しきれない部分を手動で変換
ユニットテスト結合テスト

Webシステムとかではないので、そこまで苦労せずにできたかな、という気はする。

2to3

Pythonパッケージにデフォルトで入っているツールです。
コマンドライン2to3 -w example.pyとか入力するとexample.pyが3.x系のコードに変換されて出力されます。
example.bakという形式で元のファイルも出力されるので安心です。
詳細はリファレンスをどうぞ。変換対象となる関数も記載されてます。

26.7. 2to3 - Python 2 から 3 への自動コード変換 — Python 3.6.5 ドキュメント

変換しきれない部分を手動で変換

正直、2to3で8割ぐらい変換してくれるんですが、変数に対する関数なんかはカバー仕切れないわけです。
なので、そういう部分は関数にあたりを付けて検索してひたすら置換なり削除していきます。

例えば、Python2.7では文字列がbytes型なので、何か文字列を定義するときにはmoji = u'hoge'のようにしてunicodeにしますよね?
この場合はリテラルmojiを定義していますが、u'hoge'の部分が変数だったり外部データを読み込んだり、unicodeが保証されていない場合は以下のように書くと思います。

if isinstance(moji, basestring):
    moji = moji.decode('utf-8')


上記で述べたように2.7の文字列型(basestring)はbytes型なので、decodeunicodeに変換します。
このコードを2to3にかけると以下のようになります。

if isinstance(moji, str):
    moji = moji.decode('utf-8')


3.x系ではbasestringはstrになるので、その部分は変換してくれますが、decodeは残ります。
そして、3.x系のstrはunicodeなのでdecodeを実行するとAttributeError: 'str' object has no attribute 'decode'となりエラーが出ます。

こんな感じの変数に対する関数の処理部分は自分で潰していかないといけないわけです。
手作業のほとんどがこの作業でした。

他には、割り算の結果がfloatになるとか文字列と数字の比較ができないとかも自分で潰す必要があります。
この辺りはPandasでデータを取り出すときに使ってるのでちょいちょいあるんだよな〜
しかも、検索では見つからないのでプロセスごとに実行してエラーが出たら直すという。。。

ユニットテスト結合テスト

そもそもユニットテストがちゃんと機能してないと辛いです。
案の定、今のプロジェクトはユニットテストがほぼ整備されていないので、各プロセスで出力されたファイルに対し、変換前に同プロセスで出力されたファイルの内容やハッシュを見てコードに問題がないかチェックしています。

まとめ

最初から3.x系を使おう!