Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
向函数及其签名添加参数的Python装饰器_Python_Python Decorators - Fatal编程技术网

向函数及其签名添加参数的Python装饰器

向函数及其签名添加参数的Python装饰器,python,python-decorators,Python,Python Decorators,我有多个类,其中许多类对于一个参数具有相同的初始化代码。因此,我想用包装器添加参数 由于代码已经在生产中,并且此参数是所有调用中的最后一个,但是签名具有不同的长度,并且参数只能是位置,因此从args和kwargs中“捕获”此参数并不简单 只要step是kwarg,以下“有效”,但如果不是,则它位于*args中,并将传递给函数,该函数会正确抛出,因为它有太多参数: def stepable(func): @functools.wrapps(func) def包装器(self,*args,step=

我有多个类,其中许多类对于一个参数具有相同的初始化代码。因此,我想用包装器添加参数

由于代码已经在生产中,并且此参数是所有调用中的最后一个,但是签名具有不同的长度,并且参数只能是位置,因此从
args
kwargs
中“捕获”此参数并不简单

只要
step
kwarg
,以下“有效”,但如果不是,则它位于
*args
中,并将传递给函数,该函数会正确抛出,因为它有太多参数:

def stepable(func):
@functools.wrapps(func)
def包装器(self,*args,step=1,**kwargs):
func(self,*args,**kwargs)
self.step=步骤#和其他内容,取决于步骤
返回包装器
但是,即使我使用
len(args)>len(inspect.signature(func.parameters)
(函数参数中没有
*args
显示给用户的签名是错误的(因为我使用了
@wrapps


如何添加参数(/default),以便
inspect
获得它?或者基本上是“执行
functools.partial
”的相反操作?

您的问题是
functools.wrapps
复制原始签名。在这里,您必须手动处理并更改它。如果您可以确保包装的方法中没有一个可以具有以下内容,那么If就足够简单了:

  • a
    步骤
    参数
  • *args
    (变量位置)参数
  • 一个
    **kwargs
    (VAR_关键字)参数
如果
步骤
参数没有默认值

但是无论如何,inspect模块提供了处理签名的一切

我将步骤定义为包装函数中的最后一个位置参数或关键字参数

可能代码:

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
演示:

希望这有帮助

def stepable(func):
    @functools.wraps(func)
    def wrapper(self, *args, step=1, **kwargs):
        return func(self, *args, **kwargs, step=step)
    return wrapper

class A:
    @stepable
    def __init__(self,a, b, **kwargs):
        self.a = a
        self.b = b
        for k in kwargs:
            setattr(self, k, kwargs[k])
它会将提供的步骤或1传递给函数
尽管您必须仅在实例化时将步骤作为命名参数传递,这将使代码更具可读性。

既然您说这是最后一个参数,那么
func(self,*args[:-1],**kwargs)呢;self.step=args[-1]
?感谢您的建议@a_guest!是的,这就是我想说的“但即使我会……”。但是如何向用户显示此函数接受
步骤
作为参数?您可以将其添加到
文档
字符串中。如果使用
包装
签名将保持不变。这就是它的工作原理。根据你的描述,使用装饰器是不寻常的。创建一个子类并在那里执行初始化似乎更简单。如果原始方法之一具有VAR_POSITIONAL(
*args
)参数,例如
def func(a,b=1,*args)
,该怎么办?您希望包装的函数签名是
def func(a,b=1,step=1,*args)
还是仍然是
def func(a,b=1,*args)
,其中
step
*args
的最后一个元素,不可能有默认值?@sergeballsta这是一个有效的注释,以及我编写的原因“函数[参数]中没有
*args
”。我的情况不需要它,但
def func(a,b=1,step=1,*args)
可能是合适的选择
def stepable(func):
    @functools.wraps(func)
    def wrapper(self, *args, step=1, **kwargs):
        return func(self, *args, **kwargs, step=step)
    return wrapper

class A:
    @stepable
    def __init__(self,a, b, **kwargs):
        self.a = a
        self.b = b
        for k in kwargs:
            setattr(self, k, kwargs[k])