LoginSignup
4

More than 1 year has passed since last update.

posted at

updated at

【Python2to3アップデート方法】Python2系と3系の違い

目次

概要

Python2系から3系で、互換性の無い部分について、特に気をつけなければいけない箇所についてまとめた。
ここでは、versionによる違いを、それぞれ以下の3通りの書き方と実行結果を用いて説明する。
また、2.6以降を利用していることを前提とする。

  • 2系での書き方、実行結果
  • 3系での書き方、実行結果
  • 2系で__future__モジュールを使った書き方、実行結果

__future__モジュールとは

Python2系にデフォルトで入っており、Python3で実装された互換性のない機能をPython2系で利用できるようにするモジュール。つまり、Python2系で、3系の書き方をできるようにするモジュール。

Python2系から3系に移行する際に、一時的に2系でも3系でも動くコードにしたい場合の一時対策として、必要であれば利用するのが望ましい。

futureはPython2.1で導入された比較的古いモジュールで、Pythonの旧バージョンで新バージョンの挙動を一部先に取り込みたいときに使われてきた。現在ではPython 2系と3系の挙動を近づけるために言及されることが多いが、必ずしも3系対応のために存在するのではない。

2系と3系との違い

  1. printについて (print_function)
  2. str型とunicode型について (unicode_literals)
    1. よく遭遇するエラー
  3. 割り算の少数点以下の扱いについて (division)
  4. import方法について (absolute_import)
  5. その他

1. printについて

Python3系ではprintは関数になり、予約語からも削除された。

2系

2系での書き方。

print 'version2'

3系

3系での書き方。

# 3系では以下にする
print('version3')

2系で__future__モジュール

Python2系で3系の実装をする場合の書き方。
(printは、2系でもmoduleをimportしなくても3系の書き方が可能だが)

from __future__ import print_function
print("Message")

2. str型とunicode型について

2系では、文字列型はstrとunicodeの2つがあったが、3系ではstrのみとなり、またstrとはunicodeの意味となった。つまり、2系と3系ではstr型の意味が違う。

2系

以下のコードのif文は、strとunicodeを比較することになるので、エラーとなる。

# -*- coding:utf-8 -*-
message = 'サンプル文字列'
print(type(message))

if 'あ' == u'\u3042':
    print('True')

実行結果

<type 'str'>
sample.py:5: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  if 'あ' == u'\u3042':

3系

以下のコードはstr(unicode)同士の比較なのでエラーにならない。
また、Python3ではデフォルトのcodingがutf-8になっているため、# -*- coding:utf-8 -*-を書く必要はない。(あっても問題ない)

message = 'サンプル文字列'
print(type(message))

if 'あ' == u'\u3042':
    print('True')

実行結果

<type 'str'> 3系でこれはunicodeの意味。

<type 'str'>
True

よく遭遇するエラー

2系から3系の間では、このエラーに遭遇することがよくある。

AttributeError: 'str' object has no attribute 'decode'"

もしくは

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)

これも、前述のstr型が2系と3系で違うことが原因。
以下のプログラムを2系、3系それぞれで実行してみると、以下のようになる。

# -*- coding:utf-8 -*-
raw = u'サンプル' # python3系では接頭辞uはなくてもunicodeになるので不要。合っても問題はないが。
print(type(raw))

text = raw.encode('utf-8')
print(type(text))

decoded = text.decode('utf-8')
print(type(decoded))

2系の結果

<type 'unicode'>
<type 'str'>
<type 'unicode'>

3系の結果

<type 'str'>
<type 'bytes'>
<type 'str'>

2系ではstr型をencodeするとunicode型になるが、3系でstr型をencodeするとbytes型になる。

エラーが発生する原因

3系でstrをdecodeしようとすると、以下のエラーが出る。encodeは可能。

AttributeError: 'str' object has no attribute 'decode'"

2系でstrをencodeしようとすると、以下のエラーが出る。decodeは可能。

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)

つまり、str型をdecodeする記述のあるプログラムは、2系では動くが3系では動かない。また逆に、str型をencodeする記述は、3系では動くが2系では動かない。それぞれ環境に合わせて修正する必要がある。

2系で__future__モジュール

Python2系で3系の実装をする場合。
以下のコードはunicode同士の比較なのでエラーにならない。

unicode_literalsモジュールは、prefix無しの文字列リテラルをunicodeにする。
以下のコードの'あ'は、u'あ'と同義になる。
また、こちらでは# -*- coding:utf-8 -*-が必要。

# -*- coding:utf-8 -*-
from __future__ import unicode_literals
message = 'サンプル文字列'
print(type(message))

if 'あ' == u'\u3042':
    print('True')

実行結果

2系で実行すると<type 'unicode'>となる。
3系で実行すると<type 'str'>となる。

<type 'unicode'>
True

3. 割り算の少数点以下の扱いについて

2系では、int同士の割り算は小数点以下切り捨てだったが、3系ではfloatになる。
3系で切り捨てにしたい場合は、演算子「//」 を使う。

2系

print(1/2)
print(1//2)

実行結果

0
0

3系

print(1/2)
print(1//2)

実行結果

0.5
0

2系で__future__モジュール

Python2系で3系の実装をする場合。

from __future__ import division
print(1/2)
print(1//2)

実行結果

0.5
0

4. import方法について

2系と3系ではimportの方法に違いがある。
2系では、カレントディレクトリのモジュールが優先されるが、3系では相対より絶対インポートが優先となる。これは、違うディレクトリに同名のファイルがある場合に問題となり得る。

実際に例を見たほうが早いので、以下に例を挙げる。

ディレクトリ構成

以下の構成でPythonスクリプトを作成する。

greeting.pyとlibs/greeting.pyという同名の2つのファイルがある。
libs/greeting.pyをlibs/sample_lib.pyからimport greetingとして読み込んだときに、2系ではlibs/greeting.pyがimportされるが、3系では一番上の階層のgreeting.pyがimportされる。
先述したとおり、3系のimportでは相対パスより絶対パスが優先されるためだ。

.
|-- greeting.py
`-- libs
    |-- __init__.py
    |-- greeting.py
    `-- sample_lib.py

スクリプトの中身

greeting.py

greeting.py
from libs import sample_lib

libs/__init__.py

空で作成する。

libs/greeting.py

greeting.py
def hello():
    return 'Hello'

libs/sample_lib.py

sample_lib.py
import greeting

print(greeting.hello())

2系

top階層のgreeting.pyを実行する。
libs/sample_lib.pyでlibs/greeting.pyがimportされる。

$ python greeting.py
Hello

3系

一番上の階層のgreeting.pyを実行する。
3系では、libs/sample_lib.pyで一番上の階層のgreeting.pyをimportしようとし、hello()メソッドがないためエラーとなる。

$ python greeting.py
Traceback (most recent call last):
  File "greeting.py", line 1, in <module>
    from libs import sample_lib
  File "/home/username/libs/sample_lib.py", line 3, in <module>
    print(greeting.hello())
AttributeError: module 'greeting' has no attribute 'hello'

修正方法

libs/sample_lib.pyを以下の用に修正する。

libs/sample_lib.py
-import greeting
+from . import greeting

print(greeting.hello())

これで3系でエラーが出なくなる。

$ python greeting.py
Hello

2系で__future__モジュール

libs/sample_lib.pyでabsolute_importをimportし、以下のように修正する。
(printを使っているのでprint_functionもimportする)

libs/sample_lib.py
-import greeting
+from __future__ import print_function
+from __future__ import absolute_import
+from . import greeting

 print(greeting.hello())

これでエラーが出なくなる。

$ python greeting.py
Hello

その他

以下は、2to3やfutureで検出されない2系と3系の違い。

tempfile

2系と3系では、tempfileモジュールのdefaultの書き込みモードが違う。

ver. mode
2 テキスト
3 バイナリ

Python3系で、2系同様にテキストを書き込もうとすると、以下のエラーが出る。
TypeError: a bytes-like object is required, not 'str'

3系でテキストモードで書き込む場合は、以下の様にオプションを指定する必要がある。

# -*- coding:utf-8 -*-
import tempfile

with tempfile.NamedTemporaryFile(mode='w+t', encoding='utf-8', delete=False) as tf:
    tf.write('テキスト')

参考:python3 の tempfile + write はデフォルトのままだと .write() が求められる

byte型とstring型

以下は2系のソースを3系になおすときによく使用した。

参考:Python 3 での文字列とバイト列の相互変換と16進数表示

# 文字列をbyte型に変換
'abcd'.encode()
# byte型を文字列に変換
b'abcd'.decode()

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4