2015.9.25

Python2ユーザーがPython3へ移行してハマったこと

テックチーム インターン

田畑達也


hamaru
プログラマーのトムです。偽名です。
Pythonをゴリゴリやってます。といってもJSを書いてる時間の方が長いです!

FabricはPython3系に対応していなかったので、まだPython2.6と2.7で書いていますが、新しいプロジェクトはPython3で開発しています。

もともとPython2系で開発していたので、Python3に移行してハマったことを紹介します。

Python3に移行したのは、やはりDjangoが対応したことが大きいです。
他にも、Python3ならコードキャッシュが__pycache__に作られるので邪魔になりません。

Python3で変わったこと

2系で使えていたモジュールやメソッドがなくなったり、書き方が変わったり代替方法が用意されています。
まだ変更点を全て網羅できていませんが、印象に残っている変更点を。

シンプルになったところは、親メソッドの呼び出しがシンプルになりましたね。

Python2系

super(親クラス指定, self).__init__(*args, **kwargs)

Python3系

super().__init__(*args, **kwargs)

 

これは社内のpython基礎勉強会でやりましたので飛ばしましょう。

 

逆にちょっとややこしくなったところは、『ファイルモード(パーミッション)の指定』です。

os.mkdirやos.chmodでファイルモードを指定する場合

Python2系

os.chmod(ファイルパス, 0777)

Python3系

os.chmod(ファイルパス, 0o777)

o
o?
(-o-;)

0の後ろにo(オー)を付けないと動きません。何でしょうかこのoは? 8進数octのoでしょうね。ちょっと気持ち悪いですよね。
ファイルモードは、用意されている定数で指定することもできるので定数を使うべきなのかもしれません。

このようにPython2系と3系では書き方が少し変わっています。
他にもイテレータのnextメソッドがなくなってnext関数を使うようになっていたり、Python2系のソースをPython3で動かすには、色々変更しないとだめです。

しかしこの辺は、書き方を変えるだけで簡単に対応できますね。
ではPython3のハマりどころを見ていきましょう。

文字列の型

Python3系では文字列の型が変更され、Python2系から移行した時に結構ハマります。

Python2系では、文字列はstr型とunicode型でした(raw文字列には触れない)printしてみると、”とu”で表示されたと思います。
str型のものがバイト文字列、unicode型のものがユニコード文字列でした。

Python3系では、unicode型がなくなりstr型に統一され、bytes型が追加されました。
文字列をprintすると、”とb”の二種類が型によって表示されます。
str型のものが文字列、bytes型のものがバイト列です。

Python2
バイト文字列:あ
ユニコード文字列:u'\u3042'

Python3
文字列:あ
バイト列:b'\xe3\x81\x82'

では、bytes型はどういった時にでてくるでしょう?

  • ファイルから読み込んだ時
  • コンテナなどをシリアライズした時

etc

バイナリデータや文字コードが指定されているものはbytes型を使います。

実際の使用例を見てみます。

コンテナを渡してバックグラウンド処理を実行する

Djangoのバックグラウンド処理をサンプルに記述します。

バックグラウンド起動側

from django.core.management import execute_from_command_line
user_data = {
    'id': request.user.id,
    'email': request.user.email,
}
user_data = pickle.dumps(user_data).decode('latin-1')
execute_from_command_line(['manage.py', '実行ファイル名', '--user_data=' + user_data])

--settings情報を省いています。

  • pickle.dumps(user_data)でシリアライズしたデータはbytes型で返されます。
  • 『–user_data=』というstr型の文字列にbytes型を連結することはできません。
  • bytes型をdecodeしてstr型に変換してから連結します。

※utf-8でデコードが失敗する場合は、latin-1でデコードするとうまく行きます。

バックグラウンド処理側

user_data = user_data.encode('latin-1')
user_data = pickle.loads(user_data)

※色々省いています。

起動側から受け取ったstr型をencodeでbytes型に戻し、さらにそれをデシリアライズします。

如何でしょう?ちょっとめんどくさいですよね。
しかし、bytes型のおかげで文字列まわりはすっきりしたようです。

実は私が本当にハマったのはデコードの部分です。
utf-8でデコードするとエラーがでてしまい、latin-1でデコードすることにしました。
この辺をまだまだ勉強する必要があります。

次回予告

今回はPython2からPython3へ移行した場合でしたが、PHPからPythonに移行した場合最初のハマりどころは今回同様、文字列の扱いとその他に日時周り、そして開発環境の構築だと思います。

開発環境はpyenvを使ってさっくっと用意し・・・しません!全てソースで入れましょう。次回は生で入れるPython環境構築です。たぶん・・・。

to be continued.


この記事を書いた人

テックチームインターン

田畑達也