検索
連載
よりPythonicなPythonを目指して(前編):

Python 3が後方互換性を捨ててでも求めたもの (2/2)

Python 3.0では、Python 2で書かれたスクリプトが動かなくなるような実装が行われた。なぜ、後方互換性を崩してまで大きな仕様変更を行ったのか。それは、PythonがよりPythonらしくあるためだ。

PC用表示
Share
Tweet
LINE
Hatena
前のページへ |       

8ビット文字列からユニコード文字列へ

 Python 3.0での大きな変更点の1つに、文字列型の変更があります。文字列型がユニコードベースに変更されたのです。

 Python 2までは、組み込みのデータ型「文字列型(str型)」は単なる8ビットのデータ列でした。文字列型はASCII文字列を扱うには都合が良いのですが、日本語のように多くの文字集合を持つマルチバイト文字を扱うには不便がありました。

 例えば、8ビット文字列では複数バイトで構成される文字列の境界を判別する処理などを自前で行わなければなりません。Pythonはもともと欧米で開発された言語で、日本語や中国語のような言語を扱うことは考えられていなかったのです。

 Python 1.6および2.0からは、ユニコード文字列型(unicode型)という新しい文字列型が導入されました。ユニコード文字列を使うと、日本語のような複数バイトで構成される文字の境界を簡易に、正しく扱うことができます。

 ユニコード文字列と同時に、コーデックス(codecs)という仕組みが導入され、EUC-JPやシフトJIS、UTF-8など、エンコードの変換ができるようになりました。

 Pythonはこれまで、後方互換性を守る形で機能追加を行ってきました。そのため、新しい型が追加されても、古い型はそのまま残されてきました。ユニコード文字列型が追加されたときも、文字列型はそのまま「標準の文字列型」として残されました。

 2つの文字列型があることで、マルチバイト文字列を扱うときにはユニコード型を使うというような使い分けができるようになる半面、困ることもあります。特に困るのは、ユニコード文字列から8ビット文字列に変換を行うときです。

 日本語や中国語、韓国語のような言語は、コンピュータ上で文字集合を表現するためのエンコードを複数持っています。エンコードとは、コンピュータ上の数値と表示する文字の対応を決めたルールのようなものです。

 日本語の場合は、歴史的経緯からISO-2022-JP(JIS)、EUC-JP、シフトJIS、ユニコード(UTF-8)のような複数のエンコードが場合に応じて使い分けられてきました。同じ文字でも、エンコードが異なるとまったく違う文字が表示されます。

 Webブラウザでエンコードを手動で変更すると、いわゆる「文字化け」のような状態になります。表示のときに正しいエンコードを指定しないと、たいていの場合はまったく意味をなさない文字が表示されてしまうのです。

 プログラムの中で、ustrという変数に入ったユニコード文字列を文字列型に変換するために「s = str(ustr)」としたとします。このコードには、変換に利用するエンコードの情報がありません。

 Pythonでは、このようなときに「デフォルトエンコーディング」と呼ばれる設定を利用して、エンコードの情報を取得します。標準の設定では、デフォルトエンコーディングはASCIIになっています。

 ところが、ASCIIの文字集合は127個しかありませんので、日本語のような何千と文字のある文字集合をうまく変換することができず、エラーが発生します。

 Pythonを使っていると「Unicode Decode Error」というエラーをたびたび目にします。このようにエンコード指定せずに文字列型を変換するようなコードが、このエラーの一因となっています。

 欧米で作られたソフトには、実際このように無頓着に文字列型の変換を行っている個所があります。このようなコードはASCII文字だけを扱うプログラムでは問題なく動きますが、日本語を入力すると、とたんにエラーが発生します。

 ネットワークやファイルから取り込んだ8ビット文字列をユニコード文字列に変換するときにも、似たような問題が発生することがあります。エンコードを指定しないと、マルチバイト文字列を正しく判別できないのです。

 プログラムの中で、このように文字列型の変換を行うようなコードが散らばっていると、不意にエラーが発生します。問題となるコードはプログラム中のいたるところに散らばっているので、すべてを修正するのはとても難しいことです。

 Python 3.0では、標準の文字列型がユニコード型に置き換えられました。同時に、Python 2までで使われていた文字列型は廃止されました。

 文字列型が統合されたので、コード内で複数の文字列型を変換する必要がなくなりました。不意にエンコードエラーが起こることがなくなったわけです。そういう意味では、Python 3.0では日本語のようなマルチバイト文字列をより安全に扱えるようになったといえます。

 文字列型がユニコード文字列になったので、文字列を生成するときにエンコードを指定する必要があります。Python 2.5からは、コードにエンコードを指定する必要があります。リテラルレベルの文字列については、コードを書いた時点でユニコード文字列として妥当な文字列になっているわけです。

 ファイルなどから読み込むデータについては、ファイルを開くときにエンコードを指定するようにします。Python 3.0では、ファイルオブジェクトを生成するopen()関数にエンコードを指定できるようになっていますので、引数としてエンコードを指定してファイルオブジェクトを作ります。

 マルチバイト文字列を含むファイルに、エンコード情報を与えずにopen()関数を使って開こうとすると、たいていの場合はエンコードエラーが出ることになります。

>>> f=open('some_euc.txt', 'euc-jp')
>>> s = f.read()

新しい文字列型「bytes型」

 文字列のようなオブジェクトは、画像や音声のようなバイナリデータをメモリ上に置いて、簡易に扱うために利用されることがあります。そのようなデータを扱うためには、Python 3.0のようにエンコードをかっちり決めるような文字列型は利用しづらい面があります。

 バイナリデータを扱うためには、Python 2の文字列型のように、8ビットデータをそのまま扱えるデータ型は便利です。そこで、Python 3.0ではbytes型という新しい文字列型が追加されました。bytes型は、8ビットクリーンなデータを扱えるので、Python 2の文字列型に近い機能を持っています。

 Pythonの文字列型では、メモリ利用の効率化などの目的があり、要素の削除や置き換えができません。これを「変更不可能(immutable)」といいます。Python 2の文字列型とbytes型は共に、「変更不可能」なデータ型です。

 Python 3では、bytes型に加えて変更可能なbytearrayという文字列型が追加されました。bytearray型は変更可能ですので、インデックスやスライスを使って要素を入れ替えたり、削除したりすることができます。JPEG画像のデータをbytes型に取り込み、EXIF情報を書き換えるといったことが容易に行えるようになったわけです。

 このように、Python 3.0では、古い仕様や一貫性のない仕様を取り除くだけでなく、より便利なデータ型を追加する、という仕様変更が随所に見られます。

 後編では、2種類の整数型の統合、リストを返していたメソッドや関数の仕様変更、例外の仕様変更、モジュールの統合や改名について触れる予定です。

著者紹介

柴田 淳

ウエブコア株式会社



前のページへ |       

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る