Python で任意の関数をカリー化するのは、結構面倒です (参考: Pythonでカリー化を書いてみる)。
ですが、呼び出し可能オブジェクトを利用して一見カリー化できているように見せかけることはできます。 カンタンナコト。
あぁ、そんな簡単に言うな
カリー化というとラムダ式を頑張ってこねくり回したくなりますが、この方法だと Dunder メソッドの実装を工夫するだけで同様のことができます。
import re
class Curried:
def __init__(self, f, regex=None, *args, **kwargs):
self.__f = f
self.__regex = regex if regex is not None else \
re.compile('{}\\(\\) missing \\d+ required'.format(f.__name__))
self.__args = args if args is not None else ()
self.__kwargs = kwargs if kwargs is not None else {}
def __call__(self, *arg, **kwarg):
cls = type(self)
new_args = self.__args + arg
new_kwargs = dict(**self.__kwargs, **kwarg)
try:
return self.__f(*new_args, **new_kwargs)
except TypeError as e:
if self.__regex.match(e.args[0]):
return cls(self.__f, self.__regex, *new_args, **new_kwargs)
else:
raise e
@property
def __name__(self):
return self.__f.__name__
このCurried
インスタンスは引数を一つずつ受け取りながら元の関数の引数に満ちるまでCurried
インスタンスを生成し続けます。 返るのはあくまでも関数ではありませんが、関数のように呼び出し可能なオブジェクトです。
右手上げるならその手に責任が宿ると思え
このようにして使うのだ。
def add(x, y):
return x + y
def add3(x, y, z):
return x + y + z
print(Curried(add)(2)(3))
print(Curried(add3)(2)(3)(4))
5
9
一見カリー化した関数を呼び出しているように見えますね?
オブジェクトのメソッドに対しても使えます。
class Foo(object):
def __init__(self, seed=1):
self.seed = seed
def add(self, x, y):
return self.seed * (x + y)
def multiply(self, x, y):
return self.seed * x * y
f = Foo(3)
print(Curried(f.add)(2)(3))
print(Curried(f.multiply)(2)(3))
15
18
また、参考記事ではできなかったカリー化関数 (しつこいようですが、こっちのは本当は関数ではないです) の使い回しもできます。
add2 = Curried(add)(2)
print(add2(3))
print(add2(4))
5
6