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

麦可

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

由于代码已经在生产中,并且此参数在所有调用中都位于最后,但是签名的长度不同,并且该参数只能位于位置,因此从argsand中“捕获”该参数非易事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”?

Serge Ballesta

您的问题是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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

是否可以在Python中使用装饰器向函数添加参数?

来自分类Dev

以编程方式向Python函数添加“装饰器”

来自分类Dev

向装饰器添加参数可删除cls参数

来自分类Dev

如何在TypeScript中向装饰器添加可选参数?

来自分类Dev

Python-在函数及其装饰器之间共享变量

来自分类Dev

带有装饰函数参数的python装饰器

来自分类Dev

Python装饰器处理装饰函数的默认参数

来自分类Dev

Python装饰器处理装饰函数的默认参数

来自分类Dev

具有装饰函数任意定位参数的 Python 装饰器

来自分类Dev

使用Python装饰器向方法添加方法

来自分类Dev

使用包装函数中的变量的python装饰器参数

来自分类Dev

装饰器模式-向接口添加方法

来自分类Dev

使用装饰器向字典添加功能

来自分类Dev

装饰器模式-向接口添加方法

来自分类Dev

Python装饰器提取参数

来自分类Dev

带参数的 Python 装饰器

来自分类Dev

Python函数装饰器之谜

来自分类Dev

Python装饰器函数执行

来自分类Dev

Python装饰器函数变量

来自分类Dev

Python函数装饰器之谜

来自分类Dev

装饰器添加了意外的参数

来自分类Dev

向php函数添加参数

来自分类Dev

向on(“ click”)函数添加参数

来自分类Dev

向数学函数添加参数

来自分类Dev

用装饰器处理函数参数

来自分类Dev

装饰器,允许函数接受任意参数

来自分类Dev

将参数传递给装饰器函数

来自分类Dev

装饰器,用于检查函数的参数

来自分类Dev

装饰器将参数更改为函数