如何以与 inspect.signature 一起使用的方式包装 python 函数?
How can I wrap a python function in a way that works with with 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
的参数,然后将 baz
粘贴到列表中,以某种方式动态构建 _wrapper
吗?
答案不是
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)
前面一些无争议的背景实验:
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
的参数,然后将 baz
粘贴到列表中,以某种方式动态构建 _wrapper
吗?
答案不是
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)