Pythonでpartial関数を使って関数の部分適用をする方法を解説します。
partial関数で関数の部分適用をする
partial関数は、関数と固定する引数の値を受け取ることで関数の部分適用をすることができる関数です。partial関数は、functoolsモジュールの中に含まれています。
このように引数に関数を受け取る関数は高階関数と言います。高階関数は、関数を操作するための強力なツールであり、関数の部分適用については、関数型プログラミング言語でも重要な概念です。
Pythonはマルチパラダイムの言語と言われているため、厳密ではありませんが関数型のプログラミングスタイルもサポートしており、partial関数が利用できるようになっています。
本記事では、partial関数を用いて関数の部分適用をする方法の基本について紹介します。
partial関数と同じく関数型プログラミングに関連のある関数としてmap関数、filter関数、reduce関数があります。以下のページでまとめていますので興味があれば参考にしてください。
関数の部分適用とは
関数の部分適用というのは、数学やプログラミングにおいて重要な概念です。部分適用は、ある関数に対して一部の引数を固定して新しい関数を作る手続きのことを言います。これにより関数をより柔軟に再利用したり、複雑な操作をシンプルに表現することができます。
例として、以下のような数学の関数を考えます。
\begin{eqnarray}
f(x, y) = x^{y}
\end{eqnarray}
この関数は$x$を$y$乗する関数で、Pythonでも組み込みの関数としてpow(x, y)として計算することができます。ここで、$y=2$と固定して新しい関数を作ることを考え、新しい関数を$g(x)$とおくと以下のようになります。
\begin{eqnarray}
g(x) = f(x, 2) = x^{2}
\end{eqnarray}
このように元の関数$f(x, y)$の一部の引数を固定して新しい関数$g(x)$を作成するのが部分適用の基本的な考え方です。
このような部分適用を実装する際にpartial関数を使用できます。以降で、具体的なpartial関数の使い方を説明していきます。
partial関数の基本的な使い方
partial関数の基本的な構文は以下のようになります。
from functools import partial partial_func = partial(関数, 引数=値)
partial関数は、functools
モジュール内にあるため、上記のようにインポートしてください。
partial関数は、対象の関数と関数で固定したい引数の値を「引数=値」という形で指定します。返却値は、引数を指定値で固定した関数のため、以降の処理で関数として実行できます。
基本的な使い方
上記の部分適用の説明で使用した$f(x, y) = x^{y}$において、$y$を固定する例をつかって、partial関数の使い方を見ていきます。
from functools import partial # pow(base, exp)においてexp=2に固定する pow2 = partial(pow, exp=2) # 4 ** 2 = 16 print(pow2(4)) # 5 ** 2 = 25 print(pow2(5)) # pow(base, exp)においてexp=3に固定する pow3 = partial(pow, exp=3) # 4 ** 3 = 64 print(pow3(4)) # 5 ** 3 = 125 print(pow3(5))
【実行結果】 16 25 64 125
関数は、Python組み込みのpow(base, exp)
を使用しています。
partial関数の呼び出しでは、「pow2 = partial(pow, exp=2)
」というように関数pow
と固定するexp
引数の値を指定しています。ここでポイントは、関数を渡す際に()
がないことです。関数は、pow(4, 2)
のように()
で引数を指定すると関数を実行することを意味しますが、()
がない場合は関数そのものを表します。partial関数は、受け取ったpow
を関数の内部処理で使用します。
返却値のpow2
は、そのもの自身が引数exp=2
に固定した状態のpow関数となっています。そのため、pow2(4)
とすると「$4^2=16$」ですし、pow2(5)
とすると「$5^2=25$」となります。
partial関数の便利なところは3乗の関数を作ろうと思ったら「pow3 = partial(pow, exp=3)
」とするだけで簡単に作れることです。この場合は、pow3(4)
とすると「$4^3=64$」ですし、pow3(5)
とすると「$5^3=125$」となります。
今回は、組み込み関数のpowを例にしましたがもちろん独自に作った関数等でも同様のことが可能です。このようにpartial関数を用いることで、関数の部分適用を簡単に実現することができます。
部分適用とクロージャ
部分適用と似たような言葉としてクロージャというものがあります。部分適用とクロージャは、関数の振る舞いを変更するための技術としてよく聞かれる用語ですが、それぞれ異なるコンセプトを指しています。
クロージャは、関数の外部スコープの変数を参照し、その変数の値を保持する関数のことを言います。例えば、外部の関数スコープで定義された変数を内部の関数で使用する場合は、内部関数はクロージャとして振舞うことになります。
一方で、部分適用は関数のいくつかの引数を固定し、新しい関数を生成する手法のことを指します。この新しい関数は固定されていない引数のみ受け取って動作します。
クロージャの場合は、以下のようなコード例となります。
def make_pow(factor): # my_powという内部関数は、外部関数のfactor引数を参照する def my_pow(x): return x ** factor return my_pow pow2 = make_pow(2) # 4 ** 2 = 16 print(pow2(4)) # 5 ** 2 = 25 print(pow2(5))
【実行結果】 16 25
上記例では、make_pow
関数の中に内部関数としてmy_pow
という関数を作成しています。この時、内部関数であるmy_pow
関数は外部関数のfactor
引数を参照しており、make_pow
関数は戻り値として、factor
を保持するmy_pow
関数を返却しています。
partialを使用した部分適用と上記例で示したクロージャは同じような動作をすることが分かりますが、実装方法や背後にある概念が異なっています。
部分適用は関数の引数の一部を固定することを主な目的としています。一方、クロージャは外部関数の変数を内部関数が参照・保持する機能を持っています。
まとめ
Pythonでpartial関数を使って関数の部分適用をする方法を解説しました。
partial関数は、関数と固定する引数の値を受け取ることで関数の部分適用をすることができる関数です。partial関数は、functoolsモジュールの中に含まれています。
partial関数は、map関数やfilter関数と同じように関数型プログラミングで重要な概念の関数です。Pythonはマルチパラダイムの言語と言われているため、厳密ではありませんが関数型のプログラミングスタイルもサポートしており、partial関数が利用できるようになっています。
本記事では、partial関数を使って関数の部分適用をする例を紹介しました。関数をより柔軟に再利用したり、複雑な操作をシンプルに表現する際に使用すると便利な関数のため、使い方を覚えてもらうと良いかと思います。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。