我如何包装一个python函数与inspect.signature的工作方式?



一些没有争议的背景实验:

import inspect
def func(foo, bar):
pass
print(inspect.signature(func))  # Prints "(foo, bar)" like you'd expect
def decorator(fn):
def _wrapper(baz, *args, *kwargs):
fn(*args, **kwargs)
return _wrapper
wrapped = decorator(func)
print(inspect.signature(wrapped))  # Prints "(baz, *args, **kwargs)" which is totally understandable

如何实现我的装饰器,使print(inspect.signature(wrapped))吐出"(baz, foo, bar)"?我可以动态地通过添加任何fn传入的参数来构建_wrapper,然后将baz粘到列表上吗?

答案是NOT

def decorator(fn):
@functools.wraps(fn)
def _wrapper(baz, *args, *kwargs):
fn(*args, **kwargs)
return _wrapper

给出"(foo, bar)"这是完全错误的。调用wrapped(foo=1, bar=2)是一个类型错误-缺少1个必需的位置参数:'baz'">

我觉得没必要这么迂腐,但是

def decorator(fn):
def _wrapper(baz, foo, bar):
fn(foo=foo, bar=bar)
return _wrapper

也不是我要找的答案-我希望装饰器为所有功能工作。

可以使用__signature__(PEP)属性修改包装对象返回的签名。例如:

import inspect

def func(foo, bar):
pass

def decorator(fn):
def _wrapper(baz, *args, **kwargs):
fn(*args, **kwargs)
f = inspect.getfullargspec(fn)
fn_params = []
if f.args:
for a in f.args:
fn_params.append(
inspect.Parameter(a, inspect.Parameter.POSITIONAL_OR_KEYWORD)
)
if f.varargs:
fn_params.append(
inspect.Parameter(f.varargs, inspect.Parameter.VAR_POSITIONAL)
)
if f.varkw:
fn_params.append(
inspect.Parameter(f.varkw, inspect.Parameter.VAR_KEYWORD)
)
_wrapper.__signature__ = inspect.Signature(
[
inspect.Parameter("baz", inspect.Parameter.POSITIONAL_OR_KEYWORD),
*fn_params,
]
)
return _wrapper

wrapped = decorator(func)
print(inspect.signature(wrapped))

打印:

(baz, foo, bar)

如果函数为

def func(foo, bar, *xxx, **yyy):
pass

然后print(inspect.signature(wrapped))打印:

(baz, foo, bar, *xxx, **yyy)

最新更新