能否在运行时修改函数签名并强制执行该签名?
动态修改函数签名后如何强制执行参数校验?
你已经通过修改函数的__signature__属性成功改变了函数的签名显示,但Python解释器并不会自动基于这个属性做参数合法性校验,所以需要手动在装饰器中添加参数绑定校验逻辑。
原代码问题复现
from inspect import getfullargspec, signature from functools import wraps def mock(sign): def deco(func): @wraps(func) def wrap(*args, **kwargs): print('wrap', args, kwargs) wrap.__signature__ = signature(sign) return wrap return deco def test(a, b): print(a, b) @mock(test) def func(c): print(c) print(getfullargspec(func)) # -> FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) func(1) # -> wrap (1,) {} 未抛出异常,不符合预期
解决方案:添加参数绑定校验
利用inspect.Signature的bind方法,它会严格按照签名规则绑定参数,不符合时抛出和原生函数一致的TypeError异常。修改装饰器如下:
from inspect import getfullargspec, signature from functools import wraps def mock(sign): target_sig = signature(sign) def deco(func): @wraps(func) def wrap(*args, **kwargs): # 先执行参数绑定校验,不符合签名直接抛出异常 bound_args = target_sig.bind(*args, **kwargs) # 若目标签名含默认参数,调用此方法自动填充默认值 # bound_args.apply_defaults() print('wrap', bound_args.args, bound_args.kwargs) # 可将绑定后的参数传递给原函数执行 # func(*bound_args.args, **bound_args.kwargs) wrap.__signature__ = target_sig return wrap return deco def test(a, b): print(a, b) @mock(test) def func(c): print(c) print(getfullargspec(func)) # 签名显示正常 func(1) # 抛出 TypeError: missing a required argument: 'b' func(1, 2) # 正常输出 wrap (1, 2) {}
关键说明
target_sig.bind(*args, **kwargs)会严格匹配目标签名的参数规则,缺少必填参数、参数数量不符、关键字参数不匹配等场景都会抛出TypeError,和原生函数的参数校验行为完全一致。- 如果目标签名包含默认参数,需要调用
bound_args.apply_defaults()来自动填充默认值,之后再通过bound_args.args和bound_args.kwargs获取完整的参数集合。
内容的提问来源于stack exchange,提问作者giladrv
相关产品推荐
相关产品推荐

