2008年9月17日水曜日

Python の関数はオブジェクト

1. 関数の引数に関数を渡す

「オブジェクト」が何であるかということは、とりあえず横に置いておいて...

Python では、「関数」を関数の引数に渡すことができる。

例えば、

  1. 関数を引数に取る、関数 hoge を定義し、
  2. hoge に関数 piyo を渡してみる。
def hoge(f):
    f()

def piyo():
    print "piyo"

hoge(piyo)

このように関数を引数として与えることに対して、関数型言語を学習する前は違和感を感じていた。しかし、今では関数を渡せない言語仕様の場合、

「何で関数だけ仲間はずれにするんだろう?」

と思うようになった。

 

2. Strategy パターンの代わりに使える

関数を渡して処理を進める方法は、関数を渡すことができない言語では、Strategy パターンを利用する。

Strategy パターンは、アプリケーションで使用されるアルゴリズムを動的に切り替える必要がある際に有用である。Strategy パターンはアルゴリズムのセットを定義する方法を提供し、これらを交換可能にすることを目的としている。

(Strategy パターン – Wikipedia より)

Strategy パターンを意識したコードを Python で書いてみる。

def test(a, f, b):
    print u"処理を開始します"
    print u"あれして、これして..."
    
    print u"ここからの独自処理は f に任せて..."
    result =  f(a, b)
    
    result += 1000
    print u"処理を終了します"
    return result

def add(a, b):
    print u"--- 足すぞ~ ---"
    return a + b

def subtract(a, b):
    print u"--- 引くぞ~ ---"
    return a - b

print test(10, add, 20)
print test(10, subtract, 20)

関数 test の引数 f は、具体的なストラテジーとなる関数を受け入れる。独自な処理をする部分だけ、渡された関数に処理を任せる。

上記を Java で実装する場合は、

  1. ストラテジーに相当する add, subtract をクラスにして、
  2. 同一のインターフェイスを implemnts し、
  3. 呼出し元から委譲されるようにする。

 

3. ファーストクラス オブジェクトの性質

第一級オブジェクト – Wikipedia」 によると、ファーストクラスオブジェクトの性質として、以下の 2 点が挙げれている。

  • プロシージャ関数のパラメータとして渡すことができる。
  • プロシージャや関数の戻り値として返すことができる。
  • 前者は、上例に相当する。

    後者は、関数をネストし、それを返すよう実装する。

     

    4. 関数のプロパティ

    Python では、関数は、

    呼び出し可能型 (callable type)

    に分類される。

    3.2 標準型の階層 に呼び出し可能型の説明がある。その内容に驚いた。 (@_@;

     

    a. 特殊属性

    一つは特殊属性。関数を定義すると、いくつかの属性が自動的に関数に与えられる。

    例えば、関数中にドキュメントを書いた場合、その文字列が保持される属性が存在する。

    http://www.python.jp/doc/2.4/ref/types.html

    3.2 標準型の階層 via kwout

    __module__  属性は、「関数が属しているモジュールについての情報」を持っている。この仕様は意外だった。なぜなら、「関数」に対して抱いていたイメージは、

    それ単体で無機質に存在している

    というものだったため。 Python のように、関数自身が自分の親 (のようなもの : モジュール) について知っているということに対して違和感を感じる。

    ところで、Python では、関数の中で  yield を使うとジェネレータが返される。関数は、

    ただ言われたことに対して答えを返す

    というよりは、

    クラスのように内部情報に基づいて、振る舞いを変える

    という印象を持つようになった。

    個人的に、このようなシンプルでない関数の仕様は好きになれない。

     

    b. 関数は自由にプロパティを付けられる

    Python の関数に対して、一番驚いた点は、

    関数オブジェクトはまた、任意の属性を設定したり取得したりできます。この機能は、例えば関数にメタデータを付与したい場合などに使えます。(同上より)

    関数に対して、勝手にプロパティをつけることができるの!? 試してみる。

    def hoge():
        pass
    hoge.a = u"ほげ"
    hoge.b = [1, 2, 3, 4, 5]
    
    def piyo():
        print "piyo"
    hoge.p = piyo
    
    print hoge.a
    for i in hoge.b:
        print i
    hoge.p()

    関数のプロパティに、文字列、リスト、関数などをつけてみた。結果は、

    ほげ
    1
    2
    3
    4
    5
    piyo

    おぉ~、ちゃんと表示された。JavaScript の関数オブジェクトみたい。