もっと知りたいPython3000

第2回組み込み型への変更

Pythonでは、基本的なデータ型を組み込み型(Builtin Types⁠⁠」として提供しています。文字列や数値といった基本的なデータ型だけでなく、リストや辞書、集合(Set)型のような構造を持ったデータ型が組み込み型として用意されています。

Python 2.x世代の組み込み型には、言語としての一貫性を崩す仕様がいくつか存在しているのも事実です。そのような不整合の多くは、後付けの仕様追加が原因となっています。

Python 3000では、組み込み型の設計にメスが入り、より一貫性のある仕様に見直しが図られています。今回は、組み込み型を中心に、Python 3000での変更点を概観したいと思います。

Unicodeベースの文字列型

Python 3000では、文字列型に大きな変更が加えられます。文字列(str)型がUnicodeベースの型に変更されるのです。

2.xまでは、Pythonには2種類の文字列型がありました。エンコードを考慮しない8ビット文字列としてのstr型と、Unicodeベースのunicode型です。

Python 3.0では、従来のstr型がunicode型に置き換わります。それに伴って、unicode型と、⁠u"~"」というリテラル表記は廃止されます。

標準の文字列型がUnicodeベースなったことにより、特に日本語のように複数エンコードを持つマルチバイト文字列の扱いに、多少注意が必要になります。ファイルやネットワーク経由の文字列データを元に文字列型データを生成する際には、常にエンコードを意識する必要が出てきます。

たとえば、EUC-JPエンコードで書かれたファイルを開き、読み込んだ結果をそのまま文字列型のデータに代入すると、たいてい例外が発生します。ファイルの内容を文字列に代入する時点で、Pythonのコーデックが文字列の妥当性チェックを行います。このとき、UTF-8として妥当でない文字列があると、エラーが発生するわけです。

このようなことを避けるためには、codecsモジュールのopen()などを使って、読み込み時に明示的にエンコードを指定する必要があります。

>>> import codecs
>>> f=codecs.open('foo_euc.txt')
>>> print(f.read('r', 'euc-jp'))
(ファイルの内容を表示)

8ビットデータ列としてのbytes型

エンコードを明示せず、マルチバイト文字列を8ビットのデータとして扱いたいときには、bytes型を使います。bytes型は2.xまでのstr型とほぼ同じ機能を提供している文字列型です。同時に、bytes型のリテラル表記として「b"~"」が追加されています。

Python 2.xまででは、str型とunicode型を連結することができました。連結をするとunicode型のデータを得ます。連結の過程でstr型からunicode型への暗黙の型変換が行われており、このときエンコードエラーが起こることがありました。暗黙に行われる型変換の最中にエラーがでるというのは、仕様として紛らわしいですし、あまり気持ちの良いものではありません。

Python 3.0では、標準のUnicode文字列と8ビットデータを扱うbytes型がまったく違う型として扱われます。そのため、'abc'+b'abc'のようにしてstr型とbytes型を連結できなくなっています。2つのオブジェクトを連結するためには、enocde()decode()メソッドを使って、明示的に型を合わせる必要があります。

変更可能なbytearray型

Pythonの文字列型は変更不可能(immutable⁠⁠」と呼ばれています。s[2]='c'のようにして、文字列の要素を直接変更することができません。

Pythonの文字列型は8ビットクリーンなデータをストアできたため、バイナリデータの置き場所として利用されることがありました。また、テキストデータを扱う場合でも、インデックスやスライスを使ってデータを変更できた方が便利な場合があります。このような用途に利用するため、⁠変更可能(mutable⁠⁠」なバイト列を扱うためのデータ型としてbytearrayという型が追加されています。

bytearrayにはリテラルはありません。str型やbytes型を元にして生成します。str型からbytearrayを生成するためには、エンコードを指定する必要があります。

>>> ba=bytearray('abcde', 'utf-8')
>>> ba[2]
99
>>> ba[2]=ord('C')
>>> ba
bytearray(b'abCde')
>>> ba[2:4]=b'BCDE'
>>> ba
bytearray(b'abBCDEe')

int型とlong型の統合、割り算に関する変更

Pythonの仕様変更に関する提案を記載したPEPというものがあります。Pyton 3.0では、PEP 237という比較的古いPEPに記載された提案が採用されています。2つの数値型、int型とlong型の統合です。

int型はCのlongに依存した数値型です。long型は後付けでPythonに追加された数値型で、メモリが許す限り大きな値を扱えます。Python 3.0では、2.x世代のlong型に相当する型がint型に置き換わります。これにあわせて、2.x世代で利用されていた「L」を末尾に付けるlong型のリテラル表記は廃止されます。

PEP 237が2001年に書かれていることからもわかるとおり、かなり以前から統合の提案がありました。しかし、後方互換性を崩すという理由のため、ずっと統合が見送られてきたのです。

また、Python 2.xまでは、int型同士の割り算は必ずint型を返していました。結果が小数点を含む数値となった場合は、内輪で一番近い整数を結果としていました。「1/2」の結果は「0」になります。Python 3.0では、int型同士の割り算は必ずfloat型の数値を返します。従来通りint型を得たい場合には//という演算子を使います。

辞書型への変更

辞書型に関しては、メソッドに変更が加わります。

Python 3.0では、辞書のキーを検査するためのhas_key()というメソッドが取り除かれます。2.x、3.0の互換性を保ちながらキーを検査するためには、'key' in dのようにin演算子を使います。

また、2.xまでは、キーの一覧を返すkeys()値の一覧を返すvalues()キーと値のペアを返すitems()というメソッドは、要素のコピーを作ってリストにして戻り値としていました。Python 3.0では、ビュー(view)と呼ばれるより粒度の低いイテレータ風のオブジェクトを返すように変更されます。

このため、以下のようにリストが返ることを期待したコードは3.0では動かなくなります。次のコードは、辞書の値を元に辞書の要素をソートしています。

>>> d=dict(a=1, b=2, c=0)
>>> i=d.items()
>>> i.sort(lambda x, y: cmp(x[1], y[1]))
>>> i
[('c', 0), ('a', 1), ('b', 2)]

Python 2.x、3.0両方で動くようなコードを書くには、組み込み関数sorted()を使うと良いでしょう。sorted()はPython 2.4から追加された組み込み関数で、リストやイテレータなどをソートした結果を返します。

>>> d=dict(a=1, b=2, c=0)
>>> i=sorted(d.items(), lambda x, y:cmp(x[1], y[1]))
>>> i
[('c', 0), ('a', 1), ('b', 2)]

リストを返していたメソッドがイテレータ風のオブジェクトを返すような変更は、辞書型のメソッドだけでなくPython全般にわたって実施されます。辞書以外の変更点の詳細については、次回の記事でも取り上げる予定です。

その他の変更

これまで、Pythonには2進数を扱うための簡易な方法が充実てしいませんでした。int('1010', 2)のようにして、2進数相当の文字列を整数に変換できるのみでした。

Python 3.0では、2進数を表記するための0b1010というリテラルが追加されます。また、整数を2進数相当の文字列に変換する組み込み関数bin()が追加されます。bin()の戻り値は、2進数のリテラル表記と同じ0bから始まる文字列となります。なお、8進数のリテラル表記も2進数にならい、0666から0o666のように変更になります。数字のゼロとアルファベットの「o(オー)」に続けて数値を記述するわけです。

Python 2.3から追加されたset型という組み込み型があります。set型は「集合型」とも呼ばれ、重複しない一意の要素を格納できるリストに似たデータ型です。

これまでは、set型を生成するにはset([1, 2, 3])というような表記をする必要がありました。Python 3.0では、set型にリテラル表記が追加されます。辞書のリテラルのように波括弧で要素を囲み、{1, 2, 3}のように表記します。

次回は、Pythonの言語仕様や組み込み関数、クラス、標準ライブラリに加えられる予定の変更点についてお伝えします。

おすすめ記事

記事・ニュース一覧