4
6

More than 3 years have passed since last update.

Python で任意の関数をカリー化……もどき。

Posted at

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
4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6