2009年4月30日木曜日

すべてをファーストクラスに

原文:http://python-history.blogspot.com/2009/02/first-class-everything.html
原文投稿者:Guido van Rossum

Pythonにおける私の目標の一つが、すべてのオブジェクトを「ファーストクラス」にするというものであった。これは、プログラミング言語の中のすべてのオブジェクト(数値、文字列、関数、クラス、モジュール、メソッドなど)に対して、名前をつけて、同じように取り扱うことができるようになるということを意味している。オブジェクトを変数に格納したり、リストの中に並べたり、辞書に格納したり、引数として渡したり、といったことが可能になるのである。

これを実現するためのPythonの内部実装はとてもシンプルになっている。すべてのPythonのオブジェクトは共通のC言語の構造体を元にしており、インタプリタ内のあらゆる場所で使用されている。変数やリスト、関数などは、このデータ構造のバリエーションを利用している。数値のようなシンプルなオブジェクトを表現しようとしたり、クラスのような複雑なものを実現しようとしないかぎり、この手法で大きな問題は発生しない。

「すべてをファーストクラスに」という考えを持つことは概念としてはシンプルであるが、一つだけクラスにまつわる取り扱わなければならない微妙な問題が存在する。それは、メソッドをファーストクラスオブジェクトにするという問題である。

以下のようなシンプルなPythonのクラスがあったとする(先週のブログからコピーしてきた)。

class A:
     def __init__(self,x):
         self.x = x
     def spam(self,y):
        print self.x, y

もしメソッドがファーストクラスオブジェクトであった場合は、Pythonの中の他のオブジェクトと同様に、他の変数に割り当てることが可能になるだろう。例えば、"s = A.spam"というようなPythonのコードを書くことが可能になる。この場合、変数"s"はこのクラスのメソッドを参照しているが、これは関数そのものである。しかし、メソッドは通常の関数とは完全に同じではない。特に、メソッドの最初の引数は、メソッドが定義されたクラスのインスタンスが必ず入るという想定になっている部分が異なる。

この問題を取り扱うために、「非拘束メソッド(unbound method)」というものを作成した。非拘束メソッドは、メソッドを実装している関数オブジェクトを包む薄いラッパーである。このラッパーは最初の引数がメソッドが定義されたクラスのインスタンスになるという制限を加える。もし誰かが非拘束メソッド"s"を関数のように呼び出そうとした場合には、クラス"A"のインスタンスが最初の引数として渡されなければならない。サンプルを示すと、"a = A(); s(a)" という感じになる。

関連する問題が、オブジェクトの特定のインスタンスに関連づけられたメソッドを参照するようなPythonのコードを書いたときに発生する。例えば、誰かが"a = A()"のようにインスタンスを作成し、その後"s = a.spam"と書いたとする。このとき、変数"s"クラスのメソッドを参照しているが、この参照はインスタンス"a"を通じて得られたものである。このような状況を扱うために、「束縛メソッド(bound method)」と呼ばれる、また別の呼び出し可能なオブジェクトを使用することになった。このオブジェクトもまた、メソッドの関数オブジェクトを包む薄いラッパーになっている。このラッパーは裏で、メソッドを取得するのに使用した元のインスタンスを保持している。この後のコードで"s()"と呼ぶと、暗黙のうちに最初の引数にインスタンス"a"をセットしてメソッドを呼び出す。

実際には、束縛メソッドも、非束縛メソッドも同じオブジェクトを内部で使用している。このオブジェクトは、インスタンスへの参照を保持する属性を一つ持つ。この属性がNoneにセットされていると非束縛メソッドになり、そうでない場合には束縛メソッドになるのである。

束縛メソッドと非束縛メソッドなどはそれほど重要な問題には見えないかもしれないが、これらはクラスの動作の裏側を支える重要な部分である。プログラムの中で"s.spam()"というコードが出てくると、この文は実際には2ステップに分けられて実行される。まずは"s.spam"のメソッドの検索が実行される。この検索の結果、束縛メソッドである呼び出し可能オブジェクトが返される。その次に関数呼び出し演算子である"()"がこのオブジェクトに適用され、ユーザの指定した引数も渡されてメソッドが実行されるのである。


(*)Python3000では、非束縛メソッドの考え方が削除され、"A.spam"という式は、通常の関数オブジェクトを返すようになった。結局、最初の引数をAのインスタンスの限定しても、問題の分析の役に立つことはあまりない、ということになったのである。逆に高度な使い方を使用とした場合に邪魔になることも多かったからである。この高度な使い方というのは「duck typing self」という適切な名前で呼ばれている。

0 件のコメント:

コメントを投稿