Brew-file はpython製ですが、基本自分の環境が2.7なので 3.Xの方では全然テストしてませんでしたが、 3.Xで動かない、といったissueが来たので 全面的に見なおして3.Xでも動くようにしてみました。
2.7でも引き続き使える様にPython2.XとPython3.Xの共存コードになっています。
Python2.XとPython3.Xの共存
Pythonは2.Xと3.Xで非互換な変更があり、大体のスクリプトはどちらかでしか動きません。 非互換部分が多すぎて、別途書き直したほうが良い位の事もあったりしますが、 今回、Brew-fileではは取り敢えず現状のもので両方で使える様にしてみました。
以下、今回行った変更点等。
print_function
Python2.Xで標準出力したい時などは
1
|
|
みたいにprint
文を使いますが、
これを3.Xで使うと、
File "<stdin>", line 1
print 'aaa'
^
SyntaxError: Missing parentheses in call to 'print'
な感じで怒られます。3.Xではprint
は
1
|
|
の様に関数の形になっています。 これによって大概のスクリプトは互換性を失います。
これを共存させるためには、Python2.X用に
1
|
|
を導入します。このimportを行うと、2.Xでも
print
は関数の形になります。(先の文の形は使えなくなる。)
他にも__future__
にはいくつか3.Xの動きを2.Xに取り入れるためのモジュールがあります。
中でwith_statement
なんかは2.6から導入されたwith
構文を使える様にするためのものですが、
これは3.Xの機能、というわけではなく、2.5以前の物に対してwith
を使える様にするためのモジュールです。
xrangeは無い
2.Xにあったxrange
は3.Xにはありません。
2.Xではrange
はその範囲のリストを返すもの、
xrange
はxrangeオブジェクト
と呼ばれるオブジェクトを返して
これを使うことで値を遅延評価してメモリを一気に使うことを避けることが出来ます。
なので非常に大きな範囲を使う時にはxrange
が大分高速だったりします。
3.Xではrange
が単なるリストでは無く、rangeオブジェクト
を返す様になり、
xrange
と同じような動作をするのでこれを使えば良し、と言うことに。
今回のコードではそれ程大きな数を扱うことはないので
xrange
を使ってる部分は全てrange
に置き換えて両方で使える様にしました。
速度が気になる様なレベルで使っている場合にはちょっと考えないといけません。
exceptの待受
exceptでエラーの出力を取るために、2.Xでは
1 2 |
|
的に、, e
として変数に入れていましたが、3.Xでは,
が別の意味を持つため使えず、
1 2 |
|
のようにas
を使います。
この方法は2.Xでも使えるのでこの様に変更。
map/filter
map/filterが返すものは2.Xではリストでしたが、 3.Xではそれぞれ、map、filterオブジェクトになります。
なのでこれを他のlistと直接足したりしようとすると
>>> [1, 2, 3] + map(lambda x: x + x, [1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "map") to list
な感じのエラーが置きます。
これは単にlistに変換してあげることで解決できるので、
>>> [1, 2, 3] + list(map(lambda x: x + x, [1, 2, 3]))
とキャストしてあげればOK。(基本、2.Xのコードなら、map
やfilter
の所を全てlist
でキャストしてしまっても問題ないはず。)
2.Xでもこれをしても問題ないので一通り、mapしたものをlistと結合しようとする場では キャストしておきます。
subprocessの出力のdecode
2.Xではsubprocess
からの出力をそのまま文字列として使っていましたが、
3.Xではbytes
型で帰ってくるのでこれをデコードしてやる必要があります。
(というか2.Xではbytes
はstr
と同義だったのが別物になった。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
こんな感じでp.stdout
を見るとき、
Python3の時にはそれぞれ.decode()
してstr
に変更します。
また、Python2の時には
1 2 |
|
みたいにiter
を使ったりしていましたが、この方法だと3.Xでは
b''
が延々と続いて終わりません。
素直に上のようにp.stdout
を取る方法で両方で使えます。
(かなり適当ですが取り敢えず使える方法と使えない方法ということで。)
StringIO
StringIOの置き場所が変わったので以下のように 2.X用の物を試してダメなら3.X用の物を読みこむように。
1 2 3 4 5 6 |
|
追記: 2016/04/15
ここで3.X用に
from io import StringIO
から先にtry
するように書いてましたが、
実は2.Xにもio.StringIO
は存在していて、その場合io.StringIO.write()
の
引数がunicode
になってstr
を普通に入れようとするとエラーが出てしまいます。
2.Xを先に入れると2.Xで使っていたコードがそのまま使えます。
追記ここまで
Pythonのバージョンを直接チェックして
1 2 3 4 5 |
|
みたいにすることも出来ますが、 この手の互換性を入れようとしてるコードは上の様に直接 例外処理でやってるものが見た感じほとんどです。 これだと3.Xとかでなくても、変更があった時点で切り替わるので便利なので。
urllib2
Python2.Xでurllib2
のモジュールとしてあったurlopen
等は
3.Xではurllib.request
にあります。
HTTPError
はurllib.error
に。
従ってこれらはモジュールを直接インポートするようにして、
1 2 3 4 5 |
|
な感じにしておいて使います。
参考:
Cheat Sheet: Writing Python 2-3 compatible code — Python-Future documentation