関数のリスト

例えば、0に等しいか調べる関数、...、3に等しいか調べる関数をリストにしたいとします。

preds = [
    lambda n: n == 0, lambda n: n == 1,
    lambda n: n == 2, lambda n: n == 3
]

print preds[0](3)   # False
print preds[1](3)   # False
print preds[2](3)   # False
print preds[3](3)   # True

こうすればもちろんいいですが、普通の感覚なら納得いかないでしょう。こう書くところですよね。

preds = [ lambda n: n == k for k in xrange(4) ]

print preds[0](3)
print preds[1](3)
print preds[2](3)
print preds[3](3)

これを実行すると先ほどと同じ結果が得られます。

True
True
True
True

あ、あれ?
kが3になっているからこうなるんですねえ。ありがちです。
さて、これを回避するにはどうしたらよいでしょう。少し考えて次のようにしました。

import functools
import operator

preds = [ functools.partial(operator.eq, k) for k in xrange(4) ]

print preds[0](3)   # False
print preds[1](3)   # False
print preds[2](3)   # False
print preds[3](3)   # True

functools.partialは部分適用して新たな関数を作ります(正確にはpartialオブジェクトを作るらしい)。

def f(x, y): return x * 2 + y
g = functools.partial(f, 1)     # g(y) = 2 * 2 + y
print g(3)          # 5

operator.eqは

lambda x, y: x == y

と同等なので、

g = functools.partial(operator.eq, 1)

とすれば、

lambda y: 1 == y

と同等です。
これで目的は関数のリストを作ることはできました。しかし、何か腑に落ちないというか。