如何按原函数参数定义顺序处理Python装饰器包装的函数参数?
解决方案
直接使用Python标准库inspect模块的签名绑定能力,无需手动匹配位置参数和关键字参数,即可获取和原函数参数定义顺序完全一致的完整参数字典。
核心逻辑是调用inspect.signature(func).bind(*args, **kwargs),该方法会按照原函数的参数定义规则,将传入的位置参数、关键字参数一一匹配,返回的绑定对象的.arguments属性是一个有序字典:键为参数名,值为传入的参数值,顺序和函数定义时的参数顺序完全一致,不受实际传参方式影响。
如果需要将未显式传入的默认参数也纳入结果,可以额外调用绑定对象的apply_defaults()方法。
修正后的装饰器实现
import inspect def wrapper(func): # 提前解析原函数签名,避免每次调用重复解析损耗性能 sig = inspect.signature(func) def f(*args, **kwargs): # 将传入参数绑定到原函数签名 bound_args = sig.bind(*args, **kwargs) # 可选操作:自动填充未显式传入的默认参数值 bound_args.apply_defaults() # 获取顺序与原函数定义完全一致的参数字典 all_params = bound_args.arguments # 此时按values取值的类型顺序永远和参数定义顺序一致 print([type(v) for v in all_params.values()]) return func(*args, **kwargs) return f @wrapper def foo(a, b, c, d): print(f"{a} {b} {c} {d}") foo(10, 12.5, 14, 5.2) foo(10, 12.5, d=5.2, c=14)
运行结果
两次调用打印的类型列表完全一致,符合预期:
[<class 'int'>, <class 'float'>, <class 'int'>, <class 'float'>] 10 12.5 14 5.2 [<class 'int'>, <class 'float'>, <class 'int'>, <class 'float'>] 10 12.5 14 5.2
方案优势
- 兼容所有合法Python函数传参形式:全位置传参、全关键字传参、位置+关键字混合传参、带默认值参数、
*args可变位置参数、**kwargs可变关键字参数、仅限关键字参数都能正确匹配 - 传参不合法时会自动抛出和原函数调用完全一致的
TypeError,不会改变原函数的原有行为 - 无需手动编写参数匹配逻辑,参数重名、位置参数溢出等边界情况均由标准库统一处理
内容的提问来源于stack exchange,提问作者Curious
相关产品推荐
相关产品推荐

