这是使用inspect.signature 解决此问题的新方法(适用于 Python 3.3+)。我将给出一个可以自己运行/测试的示例,然后展示如何使用它来修改原始代码。
这是一个测试函数,它只是总结了给它的任何 args/kwargs;至少需要一个参数 (a),并且有一个带有默认值 (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) # just for testing
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,那么这样做也会为 args 生成一个空元组,为 kwargs 生成一个空字典。
sum_wrapped = wrapper(silly_sum)
sum_wrapped(1, c=9, d=11)
# prints <BoundArguments (a=1, args=(), b=1, kwargs={'c': 9, 'd': 11})>
# returns 22
然后我们只需获取参数字典并在其中添加任何**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)