Mic*_*ael 2 python python-decorators
我有多个类,其中许多类的一个参数具有相同的初始化代码。因此我想用包装器添加参数。
由于代码已经在生产中,并且此参数在所有调用中都是最后一个,但签名具有不同的长度,并且参数只能是位置,因此从 和 中“捕获”此参数并args非易事kwargs。
只要step是 a ,下面的“工作”就可以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
Run Code Online (Sandbox Code Playgroud)
但即使我用(函数参数中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
Run Code Online (Sandbox Code Playgroud)
演示:
>>> 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
Run Code Online (Sandbox Code Playgroud)