【functoolsモジュールの使い方】Pythonで自作関数を自在にデザインしよう

本記事では、自作関数の利用の幅を広げるためのモジュールfunctoolsの中から、partial関数とsingledispatch関数を紹介します。

部分関数の作成: partial関数

引数を固定した部分関数を作りたい時は、functoolsモジュールのpartial関数を使います。

サンプル関数

今回サンプルとして、引数に変数xおよび二つのパラメータaとbを持ち、一次関数を返す自作関数linear_funcを用意します。

def linear_func(x, a, b):
    return a*x + b

partialで部分関数を作成しよう

linear_func関数のパラメータを固定して、部分関数を2つ作成してみましょう。

from functools import partial
linear1 = partial(linear_func, a=1, b=-1)
linear2 = partial(linear_func, a=2, b=-2)
x = np.arange(10)
print(f'linear1 = {linear1(x)}, linear2 = {linear2(x)}')
# linear1 = [-1 0 1 2 3 4 5 6 7 8], linear2 = [-2 0 2 4 6 8 10 12 14 16]

linear1とlinear2という、パラメータを固定した二つの部分関数を作成しました。これらの関数には残った引数xを与えるだけで結果を返してくれます。上ではパラメータを2つとも固定しましたが、1つだけでも大丈夫です。

linear1b = partial(linear_func, a=1)
print(f'linear1b = {linear1b(x, b=-10)})
# linear1b = [-10 -9 -8 -7 -6 -5 -4 -3 -2 -1]

関数のオーバーロード: singledispatch関数

プログラミング言語では一般に、引数の個数やデータ型が異なる同名の関数を作成することをオーバーロード(多重定義)と言います。オーバーロードは、全く同じ機能を使いたいが引数を変えたい場合に便利です。C++のような言語ではこの機能が標準実装されていますが、Pythonでは実装されていません。そこでPythonにおいて関数のオーバーロードを行う際、functoolsモジュールのsingledispatch関数を使います。

サンプル関数

引数に2をかけてそのまま返すサンプル関数を用意します。

def multiply2(a):
    a_new = 2*a
    return a_new

int型変数を渡してみよう

一つ目の例としてint型変数のval_intを渡してみましょう。

val_int = 10
print(multiply2(val_int))
# 20

想定通り2*10=20が返されました。

list型変数を渡してみよう

次に、list型変数のval_listを渡してみましょう。

val_list = [10, 20]
print(multiply2(val_list))
# [10, 20, 10, 20]

リストに2をかけた場合は、リストを2つ連結したリストが返ってきます。これはpythonにおけるリストの仕様です(数学的な演算を行う場合には基本的にnumpyの配列使うことが推奨されます)。想定していた演算結果の[20, 40]とは異なる結果となります。

同じ機能を引数のデータ型を変えて使いたい場合に、それぞれのデータ型に合わせた異なる関数(multiply2_intやmultiply2_listなど)を用意するのは効率的ではありません。

関数のオーバーロードをしよう

この問題を解決してくれるのが関数のオーバーロードです。これにより関数名を共有しながら、異なるデータ型を引数にもつことが出来ます。

from functools import singledispatch

@singledispatch
def multiply2(a):
    a_new = 2*a
    return a_new

@multiply2.register(list)
def _(a):
    a_new = [2*i for i in a]
    return a_new

多少複雑に見えるかもしれませんが、慣れればとても簡単です。

初めに特定のデータ型を想定した関数を書きます。今回はint型を想定したmultiply2を最初に定義しました。この関数をオーバーロードするためには関数の上に“@singledispatch”を書きます。

次に“@関数名.register(データ型)”を書き、下にそのデータ型に対応した関数を書きます。ここで重要な点として“@関数名.register(データ型)”を書いた時点で関数名は決定しているので、その下に書く関数の名前は(オーバーロード元の関数名以外であれば)何でも構いません。今回は単に“_(a)”と書きました。

オーバーロードした関数を使ってみよう

ではもう一度int型変数val_intと、list型変数val_listをオーバーロードした関数multiply2に渡してみましょう。

val_int = 10
print(multiply2(val_int))
# 20
val_list = [10, 20]
print(multiply2(val_list))
# [20, 40]

今回はval_intだけでなくval_listも、同じ関数multiply2で望んだ結果を得ることが出来ました。


\Pythonを基礎から勉強したい方向け「プログラミング入門コースお気軽にご相談ください

無料カウンセリングを予約する