gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 02:58:05 31 4
gpt4 key购买 nike

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

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

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

最佳答案

您的问题是 functools.wraps 复制了原始签名。在这里,您将不得不手动处理和更改它。如果您可以确定没有任何包装方法可能具有:

  • 一个步骤参数
  • *args (VAR_POSITIONAL) 参数
  • **kwargs (VAR_KEYWORD) 参数

如果step参数没有默认值

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

我会将步骤定义为包装函数中的最后一个 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

关于Python装饰器向函数及其签名添加参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60634005/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com