Pythonで関数を引数として渡した場合の挙動のまとめ(一部引数を省略した場合の挙動あり)


概要

関数を引数として扱えると、関数を変更することなく引数となる関数を変えるだけでいいので便利です。例えば、いくつかのフィルタリング関数を組み合わせて関数を作りたい時、関数を一つ一つ手作りすることなく、関数の引数となる関数の組み合わせを変えるだけでよくなります。

そこで、関数を引数として渡した場合(関数渡し)の挙動をまとめてみました。与える関数の引数を一部固定した場合についても検証してみました。


与える関数に引数を指定しない関数渡し

与える関数の引数を一切指定しない場合の関数渡しです。


test1.py

def add(a,b):

return a+b

def test1(func)
print(type(func)) # <class 'function'>
return func(1,2)

test1(add)


引数として代入されるのはfunctionクラスであることが分かりました。普通の関数と変わりなく使用できます。


与える関数の引数を一部固定した関数渡し

与える関数に引数を一部固定して渡すことができると、定数などを適宜指定して入力できるので、より便利になります。

ですが、以下のように単純に引数を入れて実行すると、引数となった関数を呼び出してから実行しようとするため、失敗します。


test2_alpha.py

def add(a,b):

return a+b

def test2(func):
print(func)
return func(b=2)

test2(add(a=1))


そこで、functoolsのpartial関数が役立ちます。partial関数は引数を一部凍結して、新たな関数を生成する関数です。partial関数を使った場合の関数呼び出しは以下のスクリプトのようになります。

partial関数の第1引数は引数を固定したい関数で、第2引数は固定したい引数を表します。これでadd関数の引数を固定して実行できるようになります。注意すべきなのはpatial関数で生成した関数で b=2 としている点です。これを省略すると、add関数の第一引数のaの方に代入しようとするので失敗します。


test2_beta.py

import functools 

def add(a,b):
return a+b

# 第一引数を凍結する関数
def add_pertial(ta):
return functools.partial(add,a=ta)

def test2(func):
print(func)
return func(b=2)

test2(add_pertial(1))



おまけ:任意の数の関数を引数とした場合

任意の数の関数を引数として与える場合は、可変長引数を使って以下のようなスクリプトで書けます。test3関数では、凍結された関数の引数を次々と足し合わせる関数になります。全く異なる種類の関数を与えることもできるので色々試してみて下さい。


test3.py

import functools 

def add(a,b):
return a+b

# 第一引数を凍結する関数
def add_pertial(ta):
return functools.partial(add,a=ta)

def test3(*funcs):
ret = 0
for func in funcs:
ret = func(ret)

return ret

test3(add_pertial(1),add_pertial(2),add_pertial(3))


何か誤字や勘違いなど不備がありましたら、ご連絡頂けると嬉しいです。


参考文献