如果您始终将参数用作位置或始终用作关键字,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')