Python使用装饰器转换函数参数时tuple不支持赋值错误如何解决
报错根因
Python 中函数的*args参数本质是只读元组,不支持直接修改元素,即使单个元素本身支持修改,也不能对元组执行下标赋值操作,这就是触发TypeError: 'tuple' object does not support item assignment的直接原因。
另外原代码逻辑存在反向错误:判断条件if type(args[i]) == args1[i]表示「参数当前类型等于目标转换类型时才执行转换」,和预期的「非目标类型时转换为目标类型」完全相反。
原有代码修复方案
只需要把元组args转为可修改的列表,同时修正判断逻辑即可,修复后代码如下:
def check_args(*args1): def decorator(func): def wrapper(*args, **kwargs): # 将只读元组转为可修改的列表 args_list = list(args) for i in range(len(args_list)): # 修正判断逻辑:当前类型不等于目标类型时再转换 if type(args_list[i]) != args1[i]: args_list[i] = args1[i](args_list[i]) result = func(*args_list, **kwargs) return result return wrapper return decorator # 测试示例 @check_args(str, str) def function(arg1, arg2): print(type(arg1), type(arg2)) print(arg1, arg2) function(5, 10) # 输出: # <class 'str'> <class 'str'> # 5 10
更完善的参数类型转换装饰器方案
上面的基础版本只支持处理位置参数,无法处理关键字参数,也没有做参数数量匹配的异常兼容,可参考下面的增强版本,支持位置、关键字参数统一处理:
import inspect from functools import wraps def type_convert(*pos_types, **kw_types): def decorator(func): # 保留原函数的元信息 @wraps(func) def wrapper(*args, **kwargs): # 获取函数参数签名,绑定当前传入的所有参数 sig = inspect.signature(func) bound_args = sig.bind(*args, **kwargs) bound_args.apply_defaults() # 处理位置参数类型转换 for idx, param_name in enumerate(list(sig.parameters.keys())[:len(pos_types)]): target_type = pos_types[idx] if not isinstance(bound_args.arguments[param_name], target_type): bound_args.arguments[param_name] = target_type(bound_args.arguments[param_name]) # 处理关键字参数类型转换 for param_name, target_type in kw_types.items(): if param_name in bound_args.arguments and not isinstance(bound_args.arguments[param_name], target_type): bound_args.arguments[param_name] = target_type(bound_args.arguments[param_name]) return func(*bound_args.args, **bound_args.kwargs) return wrapper return decorator # 测试示例 @type_convert(str, int, age=float) def user_info(name, id, age): print(f"name类型:{type(name)}, id类型:{type(id)}, age类型:{type(age)}") print(f"name={name}, id={id}, age={age}") # 混合位置参数、关键字参数调用 user_info(123, "1001", age="18.5") # 输出: # name类型:<class 'str'>, id类型:<class 'int'>, age类型:<class 'float'> # name=123, id=1001, age=18.5
内容的提问来源于stack exchange,提问作者StevieG25
相关产品推荐
相关产品推荐

