Python 2 と Python 3 のユニコード文字列、バイト列の違いメモ
Python 3 の数値をバイト列に変換する方法は bytes([i])*1。いままでとあきらかに違うことを知ったのが発端。
>>> bytes([97]) b'a'
ユニコード文字列、バイト列の違いを調査開始。
型の名称とリテラル表記
Python 2.6 は新旧両方の書き方ができるものも。
Python 2.5以前 | Python 2.6 | Python 3.0 and 3.1 | |
---|---|---|---|
ユニコード文字列型 | unicode | unicode | str |
バイト列型 | str | str or bytes*2 | bytes |
可変バイト列型 | (なし)*3 | bytearray | bytearray |
ユニコードリテラル | u'a' | u'a'*4 | 'a' |
バイトリテラル | 'a' | 'a' or b'a'*5 | b'a' |
メソッド
メソッド関連は大差ない。
Python 3 では Python 2 の string.maketrans とそっくりのスタティックメソッド maketrans がバイト列型に加わっている。そしてユニコード文字列型にも maketrans スタティックメソッドが追加された。これはユニコード文字列型の translate に渡せる辞書を返す。
>>> str.maketrans('abc', 'ABC', 'd') # Python 3 の新しい maketrans {97: 65, 98: 66, 99: 67, 100: None} >>> 'abcde'.translate(_) 'ABCe'
Python 2 で同じことをするための関数を作るとこんな感じ? Objects/unicodeobject.c を読んだり、動かしてみたりして作成してみた。
from itertools import izip def maketrans(*x): arg_length = len(x) result = {} if arg_length in (2, 3): old_chars = x[0] new_chars = x[1] none_chars = u'' if arg_length == 3: none_chars = x[2] for chars in (new_chars, none_chars): if not isinstance(chars, unicode): raise TypeError(u'must be %s, not %s' % ( unicode.__name__, chars.__class__.__name__)) if not isinstance(old_chars, unicode): raise TypeError( u'first maketrans argument must ' u'be a string if there is a second argument') if len(old_chars) != len(new_chars): raise ValueError( u'the first two maketrans arguments must have equal length') # create entries for translating chars # in old_chars to those in new_chars for old_char, new_char in izip(old_chars, new_chars): result[ord(old_char)] = ord(new_char) # create entries for deleteing chars in none_chars for none_char in none_chars: result[ord(none_char)] = None elif arg_length == 1: # x[0] must be a dict if not isinstance(x[0], dict): raise TypeError( u'if you give only one argument ' u'to maketrans it must be a dict') # copy entries into the new dict, converting string keys to int keys for old_char, new_char in x[0].iteritems(): if isinstance(old_char, unicode): # convert string keys to integer keys if len(old_char) != 1: raise ValueError( u'string keys in translate ' u'table must be of length 1') old_char = ord(old_char) elif isinstance(old_char, (int, long)): # just keep integer keys pass else: raise TypeError( u'keys in translate table must be strings or integers') result[old_char] = new_char elif arg_length < 1: raise TypeError(u'maketrans() takes at least 1 argument (0 given)') else: raise TypeError( u'maketrans() takes at most 3 argument (%d given)' % arg_length) return result
また、Python 3 では文字列型の decode メソッド、バイト列型の encode メソッドは削除されている。
数値との相互変換
chr 関数が返すものが Python 2 と Python 3 で変わっている。というか Python 2 の chr が削除され、 unichr が chr に名前を変えられたというべきなのか。
Python 2 | Python 3 | |
---|---|---|
ユニコードを数値に | ord(s) | ord(s) |
数値をユニコードに | unichr(i) | chr(i) |
バイトを数値に | ord(b) | ord(b), iter(b) |
数値をバイトに | chr(i) | bytes([i]) |
bytes([i]) と Python 2 の chr の違いは複数の値を一度に変換できるというもの。わざわざイテレータブルを要求するくらいだからできないと変か。Python 2 の chr は整数を受け取るのに対し、 bytes は整数を返すイテレータブルを受け取るという違いがある。このため Python 3 にて数値ひとつをバイトにするには bytes([i]) のようにリストを用意する必要がある。かわりに複数の数値をバイトにする際、 for 文、リスト内包、ジェネレータ式などを持ち出さなくてもよくなった。
>>> ''.join(chr(i) for i in [97, 98, 99]) # Python 2 'abc'
>>> bytes([97, 98, 99]) # Python 3 なら複数でもいける b'abc'
バイトを数値に直すには ord だが、 Python 3 の bytes 型のイテレータは数値を返すのを利用することもできる。
>>> ord(b'a') 97 >>> list(b'abc') [97, 98, 99] >>> for i in b'abc': ... print(i) ... 97 98 99 >>> print(*b'abc') 97 98 99
2009/10/31 追記。インデックスアクセスしても数値が返る。
>>> b'a'[0] 97 >>> b'abc'[0] 97
2to3.py はどう解釈するの?
2to3.py の挙動も確認してみる。
オリジナル | 2to3変換結果 |
unichr(i) | chr(i) |
chr(i) | chr(i) |
unicode | str |
str | str |
u'a' | 'a' |
'a' | 'a' |
b'a' | b'a' |
chr はそのままなのか。そして str や 'a' もそのまま。 2to3.py はバイト列として扱って欲しいものをユニコード文字列に直してしまうかもしれない。覚えておこうっと。でも、テキスト処理用のアプリ・ライブラリには都合がよいかも。
ユニコード文字列は u'a' 、バイト列は b'a' と書くようにすると Python 2.6 以降で動き、 かつ 2to3.py で Python 3 用に誤爆なしで変換できるコードになる。しかし Python 2.5 以前で動かないデメリットを上回るかどうかは不明。