もっと知りたいPython3000

第3回言語仕様、組み込み関数、クラスへの変更

Pythonでは、ソースコードの1行目、または2行目に #coding: utf-8のように記述することによって、ソースコードのエンコードを指定できます。Python 2.5からは、マルチバイト文字列を含むソースコードのエンコードを必ず明示するよう仕様が変更されています。エンコードを指定することで、インタプリタはソースコードをUnicodeとして扱うことができます。

マルチバイトについては、Python 2.xでは、文字列リテラルやコメント内に利用できるだけでした。Python 3.0からは、変数名などの識別子にUnicodeを利用できるようになりました。英数字を含むASCII文字列だけでなく、アクサンやウムラウトを含む文字、ロシア語など、より多彩な文字列を変数名やクラス名に利用できるようになっています。

このように、Python 3.0では言語仕様に近い部分にも多くの変更点があります。今回は、言語仕様やクラス、グローバル関数、標準モジュールに加えられる変更点について概観したいと思います。

nonlocal文

Pythonはとてもシンプルなスコープ(名前空間)の規則を持っています。ユーザが通常扱う範囲の名前空間は「モジュール(グローバル)スコープ」⁠ローカルスコープ」⁠ネストされたスコープ」の3つです。変数や名前を持つ関数などのオブジェクトがどのスコープに属するかは、オブジェクトが最初に定義された位置に依存します。

トップレベルで定義された変数はモジュールスコープに属します。関数内で定義された変数はローカルスコープに属します。Pythonでは、変数を定義するには代入を行います。このため、関数内のローカルスコープでモジュールスコープにある変数を更新しようとして代入すると、ローカルスコープに同名の新しい変数が作られてしまいます。このようなことを避けるため、Pythonではglobal文を使い、モジュールスコープにある変数などのオブジェクトを参照することができました。

Pytohn 3.0では、global文に似たnonlocal文が追加されます。nonlocal文を使うと、入れ子に定義された関数から、すぐ外側の(ローカル)スコープにあるオブジェクトを参照することができます。

例外に関する変更

Pythonの「例外」は、仕様の中でもあいまいさや機能の重複があると議論の的になってきた仕様のひとつです。Python 2.xで例外を受け取るには、リスト1のようにします。

リスト1 Python 2.xでの例外の受け取り
try:
    # some code
except OSError, e:
    # do something using e

Pythonはすべてがオブジェクトなので、例外もオブジェクトです。カンマの後にあるeというオブジェクトには、例外オブジェクトが代入されます。カンマの前に「OSError」とあるので、ここではOSErrorに含まれる例外のみを受け取ります。

では、1つのexcept文で複数の例外を受け取りたいときはどうすればいいでしょうか。カンマは例外の種類と例外を受け取るオブジェクトの区切りに使われてしまっているので使えません。例外を列記するにはリスト2のようにします。

リスト2 Python 2.xでの複数の例外の受け取り
          :
except (OSError, IOError), e:
    # do something using e

例外を投げるための記法のあいまいさも、やり玉にあがることが多い仕様といえます。リスト3に示す2つは、例外とオプションを指定するための記法で、どちらも同じ内容を示します。

リスト3 Python 2.xでの例外指定
raise OSError, 'foobarbaz'

raise OSError('foobarbaz')

例外に関する非直感的な仕様とあいまいさを解決するため、Python 3.0では例外の文法にメスが入ります。

まず、例外を受け取る場合。2.6、および3.0では新しいキーワード as を使ってリスト4のように書きます。

リスト4 Python 3.0での例外指定
except OSError as e:

複数の例外を1つのexcept文で受けたいときには、

except (OSError, IOError) as e

のように例外をかっこで囲んで列挙します。

print()関数、exec()関数

Pythonは「式」「文」の間に明確に区切りを設けています。「式」とは関数呼び出し、比較などを行う構文です。リスト内包表記やジェネレータ式も式の仲間です。1つの行には複数の式を書くことができます。

「文」とはforやif、関数定義を行うdefなどを指します。プログラムの流れを変えたり、新しいオブジェクトを生成するための命令です。文には改行が必要です。Pythonでは代入(変数定義)も文です。文は1行に1つしか書けませんので、C言語のようにif文やfor文の中で変数定義をすることができません。

Python 2.xまでは、printexecとして定義されていました。特定の処理を行うための命令が、ifやforと同じ文として定義されていることは言語としての一貫性を崩すということで、かつてより議論の対象となることがありました。

Python 3.0では、printとexecが組み込み関数として再定義されます。関数ですので、print('foo') のように後ろにかっこをつけて呼び出す必要があります。

printが文であったときに特に評判が悪かったのが、出力先の指定方法です。printは特に指定をしないと標準出力に結果を出力します。出力先を変えたいときには、リスト5のようにする必要がありました。

リスト5 Python 2.xでprintの出力先指定を変える
print >>>sys.stderr, "Some error occured!"

3.0のprint()関数では、引数として出力先を与えます。

リスト6 Python 3.0のprint()の出力先指定
print("Some error occured!", file=sys.stderror)

その他、末尾に追加する改行の変わりに使う文字列を指定する「end⁠⁠、カンマで区切って渡す引数のセパレータを指定する「sep」など、ほかにも引数が使えるようになっています。

イテレータを返すようになる組み込み関数

前回、辞書型のメソッドkeys()、values()などの変更について書きました。2.xではリストを返していたこれらのメソッドは、3.0ではイテレータ風のviewと呼ばれるオブジェクトを返します。

同様の変更が、リストを返す組み込み関数の返り値に対しても施されます。たとえば、Python 3.0では組み込み関数のrange()はイテレータ風のrangeオブジェクトを返すようになります。それに伴って、xrange()は廃止されます。xrange()はもともと、すべての要素を含む粒度の高いリストオブジェクトを返すrange()の代わりにイテレータ風のオブジェクトを返す関数として用意されたものでした。ループを作るためなどに大きなサイズのシーケンスを生成するときにはxrange()を使っていましたが、range()がイテレータを返すようになって、必要がなくなったわけです。

また、map()、filter()、zip()などもイテレータを返すようになります。range()を含めこのような関数の戻り値がリストであることを期待して、sort()などのメソッドを呼んだり、インデックスやスライスで要素を取り出そうとするとエラーになります。

メタクラスに関する変更

メタクラスという高貴な響きを持つ言葉の全体像を短く説明するのは大変難しいことです。多少乱暴に言うと、クラス生成をフックすることで、インスタンスの生成時にいろいろな処理を行う仕組みをメタクラスと呼びます。ダイナミックな型付け言語としての特長を生かし、クラス設計の抽象性、柔軟性を増すことによって、開発をより効率化することができます。

メタクラスを使うと、状況に合わせてクラスの振る舞いを変更したり、動的に機能を追加できます。データベース上のデータをPythonのオブジェクトとして扱えるようにするO/Rマッパーは、単純な設定をするだけで豊富な機能を持ったクラスを生成できますが、このようなことができるのがメタクラスの利点の1つといえます。

Python 3.0では、メタクラスの仕組みをより強力にサポートし、より一貫性のある仕様にするため、いろいろな変更が計画されています。

メタクラスを作るには、typeを継承したクラスを作り、__new__() という初期化を行うメソッドを定義します。2.xでは、クラスアトリビュート __metaclass__ にtypeを継承したクラスを代入することで、このクラスをインスタンス生成時に呼び出す指定をしていましたリスト7⁠。

リスト7 Python 2.xでのメタクラスの作成
class Klass:
    __metaclass__ = MetaKlass
             :

Python 3.0からは、特別なアトリビュートを使う変わりにクラス定義時にキーワード引数を渡す、というよりきれいな記述方法になっています。クラスを継承する際には、メタクラス指定のキーワード引数の前に親クラス名を指定します。

リスト8 Python 3.0でのメタクラス作成
class Klass(metaclass = MetaKlass):

その他、クラス定義時にKlass(*baseclasses) のようにベースクラスをリストで渡したり、 **kws のようにキーワード引数を辞書渡しする記法も可能になっています。ベースクラスやメタクラスに渡すキーワード引数を動的に指定できるようになるわけです。typeを継承するメタクラスには、__prepare__ というクラス本体の評価前に呼ばれる特殊メソッドが追加されました。

関数への注釈(Function Annotations)

Python 3.0では、関数の引数などに注釈を付けるためのFunction Annotationsという記法が導入されます。これまで、関数やメソッドの解説は、def文の直後に置くドキュメンテーション文字列(Doc String)に記述してきました。それと同じように、引数や、関数の戻り値に対して注釈を付けることができるようになります。リスト9のような記法になります。

リスト9 注釈の入れ方
def func(arg1 : "引数の説明1",
         arg2 : "引数の説明2" = 1) -> "返り値の説明":
    # 関数定義

コロンや -> の後に置く文字列は、辞書オブジェクトに格納されます。実際は記述してある表記を式として評価して辞書に格納されます。文字列だけでなく関数呼び出しや、関数オブジェクト(関数名)を置くこともできます。

Function Annotationsの辞書は、関数の持つ__annotations__ という辞書に格納されます。文字列の変わりに引数チェック用の関数オブジェクトを指定し、デコレータと組み合わせることによって、手軽に引数や返り値の型チェックが行えるようになるわけです。

その他の変更

その他の変更点について、手短にまとめてみます。

比較に関する変更
「!=」と同じ意味を持つ「<>」が使えなくなります。
raw_input()の廃止
raw_input()相当の機能がinput()に置き換わります。入力内容をevalするinput()はなくなります。
グローバル関数の廃止
apply()、callable()、coerce()、file()、reduce()、reload()がグローバル関数から削除されます。
クラスデコレータ
関数デコレータと同じ表記で、クラスにデコレータを追加できるようになります。
モジュールの統廃合、改名
PEP 8に沿わない名前を持つモジュールが改名されます。また、cPickleがpickleになるなど、一部モジュールが廃止されます。再配置が行われるモジュールもいくつかあります。

次回は、Python 2.xから3.0への移行方法や、2.xと3.0間で互換性を保つための方法について解説します。

おすすめ記事

記事・ニュース一覧