パラメータを常にポジショナルとして、または常にキーワードとして使用している場合、Thorsten ソリューションは正常に機能します。ただし、パラメーターがどのように渡されるかに関係なく、パラメーターに同じ値を与える等しい呼び出しを検討したい場合は、より複雑なことを行う必要があります。
import inspect
def make_key_maker(func):
args_spec = inspect.getargspec(func)
def key_maker(*args, **kwargs):
left_args = args_spec.args[len(args):]
num_defaults = len(args_spec.defaults or ())
defaults_names = args_spec.args[-num_defaults:]
if not set(left_args).symmetric_difference(kwargs).issubset(defaults_names):
# We got an error in the function call. Let's simply trigger it
func(*args, **kwargs)
start = 0
key = []
for arg, arg_name in zip(args, args_spec.args):
key.append(arg)
if arg_name in defaults_names:
start += 1
for left_arg in left_args:
try:
key.append(kwargs[left_arg])
except KeyError:
key.append(args_spec.defaults[start])
# Increase index if we used a default, or if the argument was provided
if left_arg in defaults_names:
start += 1
return tuple(key)
return key_maker
上記の関数は、キーワード引数 (およびデフォルト) を位置指定にマップしようとし、結果のタプルをキーとして使用します。少しテストしましたが、ほとんどの場合、適切に動作するようです。ターゲット関数も**kwargs
引数を使用すると失敗します。
>>> def my_function(a,b,c,d,e=True,f="something"): pass
...
>>> key_maker = make_key_maker(my_function)
>>>
>>> key_maker(1,2,3,4)
(1, 2, 3, 4, True, 'something')
>>> key_maker(1,2,3,4, e=True) # same as before
(1, 2, 3, 4, True, 'something')
>>> key_maker(1,2,3,4, True) # same as before
(1, 2, 3, 4, True, 'something')
>>> key_maker(1,2,3,4, True, f="something") # same as before
(1, 2, 3, 4, True, 'something')
>>> key_maker(1,2,3,4, True, "something") # same as before
(1, 2, 3, 4, True, 'something')
>>> key_maker(1,2,3,d=4) # same as before
(1, 2, 3, 4, True, 'something')
>>> key_maker(1,2,3,d=4, f="something") # same as before
(1, 2, 3, 4, True, 'something')