Python2のstrは死んだ
Python3の概略をざっと聞くと、Python3ではPython2のユニコード型が文字列型になり、文字列型はバイト文字列型になった、というイメージを持ってしまう人も多いだろう。Python2の s="spamspamspam"
は、Python3の s=b"spamspamspam"
と同じだ、と。
しかし、Python3に"バイト文字列"なんて存在しない。あるのは "bytes
"だ。複数形を示す "s"に注意しよう。bytes
はバイトのコンテナであり、バイトとは整数値だ。バイトは文字ですらないのだ。
そう、だからもう認めよう。Python2のstr
は死んだ。Python3にはもう存在しない。str
は消え去った。お亡くなりになった。お隠れになった。成仏した。主の御許に召された。バージョン履歴に残るゴミクズとなった。その生涯は幕を閉じ、アンコールに応じることもないのである。
strとbytesの違い
とは言っても、Python2のstr
とPython3のbytes
で類似点は多い。特にbytes
型のメソッドはstr
のメソッドがほぼそのまま残されており、使えなくなっているのはencode()
メソッドぐらいだ。
まずこの例を見てみよう。
s = b"abc" print(s[0]+s[1])
Python2.6/2.7でこのスクリプトを実行すると、結果は"ab"
となる。しかし、Python3ではなんと195
となるのだ。195
は、ord('a')+ord('b')
の値だ。つまりPython3のbytes
オブジェクトでは、bytes_obj[idx]
で"文字"ではなく、数値が返るのである。
>>> b'spam'[0] 115
イテレータでbytes
オブジェクトの要素を取得しても同様だ。
>>> for c in b"spam": ... print(c) ... 115 112 97 109
str
型やunicode
型では、インデックスで要素を取得すると同じ型の値が返る。str_obj[0]
はstr
型の値となるし、unicode_obj[0]
はunicode
型のオブジェクトの値が返る。しかし、bytes
型の場合には、byes
型のオブジェクトではなく、int
型のオブジェクトが返ってくるのだ。これは従来の文字列型とは全く異なる点で、よく理解しておかなければならない。
str
型やunicode
型は、文字列を表すプリミティブ型だが、bytes
型はそうではない。int
型オブジェクトを格納するコンテナオブジェクトにすぎないのだ。
このbytes
型の動作は、文字列オブジェクトよりはarray
モジュールのarray.array('B')
オブジェクトとほぼ同じだ。bytes
型は、Python2の文字列型と考えるより、array.array('B')
にstr
型のメソッドを追加したもの、と考えた方が類似点は多いのである。
このため、通常、文字列で使うイディオムでbytes
型では使えないものは多い。
b"spam"[0] == b"s"
は当然False
だ。b"spam"[0]
の値は数値の115
であり、bytes
オブジェクトのb"s"
と同じ値であることはありえない。このような場合は
b"spam"[0] == ord("s")
と明示的に数値で比較するか、
b"spam"[0] == b"s"[0]
とどちらもインデックスで値を取得する、もしくは
b"spam"[0:1] == b"s"
のように、スライスで値を取得した場合はbytes
オブジェクトが返るのを利用する。
また、
b = bstr[0:5]+bstr[7]+bstr[9:]
も駄目だ。
b = bstr[0:5]+bstr[7:8]+bstr[9:]
としなければならない。
他にも、
b"-".join(b"spam")
もb"s-p-a-m"
にはならない。bytes.join()
に渡せる引数は、bytes
オブジェクトを返すイテレータだけだからだ。こう書きたければ、
b"-".join(bytes([c]) for c in b"spam")
とでもするしかないだろう。
ユニコードとの自動変換
Python3では、bytes
型とstr
型(Python2でのunicode
型)の自動変換が廃止されている。
u"spam" + b"ham"
はPython2ではu"spamham"
だが、Python3ではTypeError
となる。また、Python2で
>>> print(unicode(b"spam")) spam
であるが、Python3ではbytes
型でも数値やリストと同様に
>>> print(str(b"spam")) "b'spam'"
となってしまう。これは、str
に渡されたオブジェクトは全てrepr()
で文字列に変換されてしまうからだ。byte
オブジェクトをstr
に変換する場合は、かならずエンコーディングを指定しなければならない。
>>> print(str(b"spam", "ascii")) spam
bytes
オブジェクトとstr
オブジェクトの比較では==
による比較だけが許されており、
u"spam" == b"ham"
はPython2ではTrue
だが、Python3ではFalse
となる。このため、Python2では{b'spam':1, u'spam':1}
は
>> {b'spam':1, u'spam':1} {'spam': 1}
と長さ1の辞書となっていたが、Python3では
>> {b'spam':1, 'spam':1} {'spam': 1, b'spam': 1}
のように長さ2の辞書となる。