メソッドに出来るもの
個人的にfunctools.partialを気に入っています。
itertoolsもそうですが、Pythonを関数型言語っぽく使えるので。
でも、残念ながら、functools.partialが返すものは厳密には関数じゃないみたいなんです。
前、どこかで見たのですが、メソッドにするときに、振る舞いの違いが露見するのです。
実際に確かめてみます。
from __future__ import with_statement, division, print_function from functools import partial class CallableClass(object): def __call__(*a): return a class A(object): def m1(*a): return a m2 = lambda *a: a def f(*a): return a m3 = partial(f, 1) m4 = CallableClass() a = A() print(a.m1("hello!")) print(a.m2("hello!")) print(a.m3("hello!")) print(a.m4("hello!")) #実行結果 #(<__main__.A object at 0x00B04C50>, 'hello!') #(<__main__.A object at 0x00B04C50>, 'hello!') #(1, 'hello!') #(<__main__.CallableClass object at 0x00B04C30>, 'hello!')
普通の関数のm1のと、lambdaのm2は、全く同じ振る舞いをしていることがわかります。
一方、functools.partialのm3、__call__を定義したクラスのm4には、
メソッドを呼び出されたインスタンス(普通selfにするやつ)が渡されていない事がわかります。
この差異が問題になるのは、関数デコレータをメソッドに対して使うとき。
普通の(メソッドではない)関数ならば、ぶっちゃけ呼び出し可能であれば、
真の関数でなくても問題になりません。
しかし、メソッド用のデコレータは、絶対に関数を返さないと、まずい事になります。
たとえば、こんなメモ化デコレータ
from __future__ import with_statement, division, print_function class Memo(object): def __init__(self, func): self.func = func self.dict = {} def __call__(self, *a, **kw): key = tuple(a) + tuple(sorted(kw.iteritems())) if key in self.dict: return self.dict[key] else: value = self.func(*a, **kw) self.dict[key] = value return value @Memo def fib(n): if n in [0, 1]: return 1 else: return fib(n - 1) + fib(n - 2) class Fib(object): @Memo def fib(self, n): if n in [0, 1]: return 1 else: return self.fib(n - 1) + self.fib(n - 2) print(fib(30)) print(Fib().fib(30))