Python Tips: 関数の合成をしたい

Python で関数の合成を行う方法をご紹介します。

「関数の合成」とは、数学でいう関数合成と同じような形でふたつの関数を合体させるような処理のことです。

数学での表記はこんな感じだったでしょうか。

f.g(x) = f(g(x))

以下、これを Python で行う方法を見ていきます。

まずは、ひとつめの関数からふたつめの関数へと受け渡しされる値がひとつの場合から。

引数がひとつだけの場合

# ふたつの関数を合成するための関数を定義する
def compose1(outer_func, inner_func):
    return lambda x: outer_func(inner_func(x))

# abs と int を合成して新たな関数 abs_int を作成する
abs_int = compose1(abs, int)

# 実際に使ってみる
print(abs_int("-12"))  # => 12
print(abs_int(-4.3))   # => 4

compose1 という関数を使って新たに定義した abs_int は、引数を整数に変換してからさらに絶対値に変換して返す関数となりました。

ただし、この関数では次のように複数の引数を取るケースに対応できません。

abs_sum = compose1(abs, sum)
print(abs_sum(-5, -10))  # => TypeError

もう少し汎用的なものを次に見てみます。

引数が複数の場合

次の compose2 は引数が複数の場合にも、キーワード引数がある場合にも対応しています。

# ふたつの関数を合成するための関数を定義する
# こちらは引数が複数個ある場合でも大丈夫
def compose2(outer_func, inner_func):
    def composed(*args, **kwds):
        return outer_func(inner_func(*args, **kwds))
    return composed

# max の結果に abs を適用する関数を作る
abs_max = compose2(abs, max)
print(abs_max(-5, -10))  # => 5

これを使えば、たとえば標準入力から立て続けに整数を取得したい場合なんかに次のように書くことも可能です。

# input の結果に int を適用する関数を作る
questions = ["age: ", "height: ", "weight: "]
ans = map(compose2(int, input), questions)
qa = {q: a for q, a in zip(questions, ans)}
print(qa)  # => age height weight のキーと値を持った辞書

使いどころは決して多くはないかもしれませんが、ここぞというときに使えるようにしておくと便利かなと思います。

以上です。

参考