我有多个类,其中许多具有一个参数的相同初始化代码。因此,我想在包装器中添加参数。
由于代码已经在生产中,并且此参数在所有调用中都位于最后,但是签名的长度不同,并且该参数只能位于位置,因此从args
and中“捕获”该参数并非易事kwargs
。
以下“作品”(只要step
是即可)kwarg
,但如果不是,则在其中*args
,并将被传递给该函数,该函数会正确抛出,因为它包含太多参数:
def stepable(func):
@functools.wraps(func)
def wrapper(self, *args, step=1, **kwargs):
func(self, *args, **kwargs)
self.step = step # and other stuff, depending on step
return wrapper
但是,即使我抓住了它len(args)>len(inspect.signature(func).parameters)
(*args
函数参数中没有),向用户显示的签名也是错误的(因为我使用了@wraps
)。
我如何添加参数(/默认),以便inspect
得到它?还是基本上是“做逆functools.partial
”?
您的问题是functools.wraps
复制原始签名。在这里,您将必须手动处理和更改它。如果可以确定是否所有包装方法都可以包含以下内容,则可能足够简单:
step
参数*args
(VAR_POSITIONAL)参数**kwargs
(VAR_KEYWORD)参数如果step
参数没有默认值
但是无论如何,检查模块提供了处理签名的所有功能。
我将步骤定义为包装函数中的最后一个POSITIONAL_OR_KEYWORD参数
可能的代码:
def stepable(func):
oldsig = inspect.signature(func)
# search if a VAR_POSITIONAL or VAR_KEYWORD is present
# if yes insert step parameter before it, else insert it in last position
params = list(oldsig.parameters.values())
for i, param in enumerate(params):
if param.kind == inspect.Parameter.VAR_POSITIONAL:
break
if param.kind == inspect.Parameter.VAR_KEYWORD:
break
else:
i = len(params)
# new parameter name is step or step_[_...] if step if already present
name = "step"
while name in oldsig.parameters:
name += '_'
newparam = inspect.Parameter(name,
inspect.Parameter.POSITIONAL_OR_KEYWORD,
default = 1)
params.insert(i, newparam)
# we can now build the signature for the wrapper function
sig = oldsig.replace(parameters = params)
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
bound = sig.bind(self, *args, **kwargs) # compute the bound parameter list
bound.apply_defaults()
step = bound.arguments[name] # extract and remove step
del bound.arguments[name]
cr = func(*bound.args, **bound.kwargs) # call original function
self.step = step
return cr
wrapper.__signature__ = sig
return wrapper
演示:
>>> class A:
@stepable
def func(self, a, b=1):
"""This is a test"""
print(a,b)
>>> a = A()
>>> a.func(5)
5 1
>>> a.step
1
>>> a.func(5,6)
5 6
>>> a.step
1
>>> a.func(5,6,7)
5 6
>>> a.step
7
>>> help(a.func)
Help on method func in module __main__:
func(a, b=1, step=1) method of __main__.A instance
This is a test
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句