这里是一种较新的方法来解决这个问题,使用
inspect.signature
(适用于Python 3.3+)。首先,我会给出一个可运行/测试的示例,然后展示如何使用它修改原始代码。
这是一个测试函数,它只是将给定的任何args/kwargs相加;至少需要一个参数(
a
),并且有一个带有默认值的关键字-only参数(
b
),以测试函数签名的不同方面。
def silly_sum(a, *args, b=1, **kwargs):
return a + b + sum(args) + sum(kwargs.values())
现在让我们为
silly_sum
创建一个包装器,它可以像调用
silly_sum
一样被调用(有一个例外,我们将在下文说明),但是只将kwargs传递给包装的
silly_sum
函数。
def wrapper(f):
sig = inspect.signature(f)
def wrapped(*args, **kwargs):
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
print(bound_args)
all_kwargs = bound_args.arguments
assert len(all_kwargs.pop("args", [])) == 0
all_kwargs.update(all_kwargs.pop("kwargs"))
return f(**all_kwargs)
return wrapped
sig.bind
返回一个BoundArguments
对象,但是这不会考虑默认值,除非您显式调用apply_defaults
。如果没有传入*args
/**kwargs
,则这样做也会生成一个空元组和一个空字典。
sum_wrapped = wrapper(silly_sum)
sum_wrapped(1, c=9, d=11)
然后,我们只需要获取参数的字典并添加任何**kwargs
。不使用此包装器的例外情况是无法将*args
传递给函数。这是因为没有这些参数的名称,所以我们无法将它们转换为 kwargs。如果将它们作为名为 args 的 kwarg 传递是可接受的,那么可以这样做。
以下是如何将其应用于原始代码:
import inspect
class mydec(object):
def __init__(self, f, *args, **kwargs):
self.f = f
self._f_sig = inspect.signature(f)
def __call__(self, *args, **kwargs):
bound_args = self._f_sig.bind(*args, **kwargs)
bound_args.apply_defaults()
all_kwargs = bound_args.arguments
assert len(all_kwargs.pop("args"), []) == 0
all_kwargs.update(all_kwargs.pop("kwargs"))
hozer(**all_kwargs)
self.f(*args, **kwargs)