環境
- python 3.8
問題
functools.partialを使って、関数を部分適用したいです。
以下のようなコードを書いたら、"TypeError: add() got multiple values for argument 'a'"というエラーが発生しました。
from functools import partial
def add(a:int, b:int) -> int:
return a + b
# 引数`a`をキーワード引数として指定する
add_a1 = partial(add, a=1)
# a=1,b=2,result=3 を期待していた
result = add_a1(2)
# TypeError: add() got multiple values for argument 'a'
いろんなパターンを試す
以下のようなコードの場合、エラーは発生しませんでした。
# 引数`a`を位置引数として指定する
foo = partial(add, 1)
result = foo(2)
print(result)
# => 3
add_a1 = partial(add, a=1)
# 引数`b`をキーワード引数として指定する
result = add_a1(b=2)
print(result)
# => 3
partial関数の中身を見る
functools.partial関数のドキュメントによると、partial関数の中身は以下のコードと等価です。
def partial(func, /, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = {**keywords, **fkeywords}
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
エラーが起きたときのコードは add(2, a=1)
を実行していたので、"TypeError: add() got multiple values for argument 'a'" というエラーが発生していたようです。
まとめ
部分適用した後の関数が、位置引数で指定できないのは少し不便です。
# NG -> 不便
add_a1(2)
# OK
add_a1(b=2)
部分適用した後に残る引数(上記だと引数b
)より前の引数は、partial
関数で部分適用した関数を作る際、位置引数として指定するのがよいと思います。
def add(a,b,c):
return a+b+c
# 引数aを残す
foo1 = partial(add, b=2, c=3)
foo1(1)
# => 6
# 引数bを残す
foo2 = partial(add, 1, c=3)
foo2(2)
# => 6
# 引数cを残す
foo3 = partial(add, 1, 2)
foo3(3)
# => 6