Python装饰器传*args/**kwargs报错,为何无法直接传**kwargs?
问题
刚接触Python装饰器,想要实现一个装饰器函数,在执行被装饰函数前验证用户指定的关键字参数是否合法。被装饰函数可接受2个或3个参数,但在装饰器的wrapper函数else分支中,直接传递**kwargs会触发UnexpectedArgument错误,只能逐个传递args中的元素,对此存在疑问。相关代码如下:
@staticmethod def verify_quantity(func): def wrapper(*args, **kwargs): valid_quantities = ['voltage', 'current', 'power'] try: quantity = kwargs['quantity'] args = list(kwargs.values()) except KeyError: quantity = args[0] if quantity.lower() not in valid_quantities: gf.report_msg(f'Parameter "{quantity}" is not a valid scalar quantity. Valid options:', level='error') for valid_quantity in valid_quantities: print(f' > {valid_quantity}') else: if len(args) == 2: return func(args[0], args[1]) elif len(args) == 3: return func(args[0], args[1], args[2]) return wrapper @staticmethod @verify_quantity def scalar_to_bytes(quantity: str, value: (float, int), num_bytes: int) -> list: if quantity.lower() == 'current': const = 1e4 else: const = 1e3 scaled_value = int(const * value) min_bytes = (len(hex(scaled_value)) - 2) // 2 if num_bytes < min_bytes: gf.report_msg(f'Minimum bytes required for conversion: {min_bytes}', 'warning') num_bytes = min_bytes byte_list = list(scaled_value.to_bytes(num_bytes, 'little')) return byte_list
解答
报错原因
直接传**kwargs触发错误的核心原因:
- 当用户用位置参数调用函数时,
kwargs是空字典,传递**kwargs等于不给原函数传任何参数,而原函数需要2-3个参数,必然触发参数缺失的错误。 - 装饰器逻辑修改了
args的原始含义:原本args是位置参数元组,在try块里被替换成kwargs.values()的列表,导致参数传递逻辑混乱,无法兼容位置、关键字、混合等多种传参方式。 - 现有代码只处理了
args长度为2或3的情况,其他场景(比如混合传参时args长度为1)会直接跳过函数调用,引发异常。
修正后的装饰器实现
正确的做法是保留原函数的参数传递逻辑,仅提取quantity做验证,验证通过后直接传递原始参数给原函数:
@staticmethod def verify_quantity(func): def wrapper(*args, **kwargs): valid_quantities = ['voltage', 'current', 'power'] # 提取quantity参数:优先取关键字参数,无则取第一个位置参数 if 'quantity' in kwargs: quantity = kwargs['quantity'] else: if not args: gf.report_msg('必须提供quantity参数', level='error') return quantity = args[0] # 验证合法性 if quantity.lower() not in valid_quantities: gf.report_msg(f'参数"{quantity}"不是合法的标量类型,合法选项:', level='error') for valid_q in valid_quantities: print(f' > {valid_q}') return # 验证通过,直接传递原始参数给原函数 return func(*args, **kwargs) return wrapper
说明
- 该实现兼容位置参数(如
scalar_to_bytes('voltage', 12.5, 2))、关键字参数(如scalar_to_bytes(quantity='current', value=0.5))、混合传参等所有调用方式。 - 无需手动判断参数长度,原函数的参数定义(包括是否有默认值)会自动处理参数数量的问题,逻辑更健壮。
内容的提问来源于stack exchange,提问作者Justin Dubin
相关产品推荐
相关产品推荐

