Python中@patch装饰器无法使用关键字参数的替代方案咨询
解决unittest.mock @patch装饰器下关键字参数调用报错问题
在Python 3.11环境中使用unittest.mock的@patch装饰器时,若被装饰函数通过关键字参数调用会触发TypeError,提示“got multiple values for argument 'arg'”,但位置参数调用正常。
原代码示例
import traceback from unittest.mock import patch import os.path @patch('os.path.isfile') def being_patched(arg, isfile_mock): print(f"Inside being_patched. arg={arg} isfile_mock={isfile_mock}") if __name__ == '__main__': being_patched(True) # 正常运行 print("\n----------\n") try: being_patched(arg=True) # 报错 except TypeError: traceback.print_exc()
错误输出
Inside being_patched. arg=True isfile_mock=<MagicMock name='isfile' id='3181488491344'> ---------- Traceback (most recent call last): File "C:\Users\Mike Ulm\PycharmProjects\Exercises\USDataMap\patch_after_kwargs.py", line 13, in <module> being_patched(arg=True) File "C:\PythonFiles\Python311\Lib\unittest\mock.py", line 1375, in patched return func(*newargs, **newkeywargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: being_patched() got multiple values for argument 'arg'
问题原因
@patch装饰器默认会将mock对象作为位置参数注入到被装饰函数的参数列表最前面。当用being_patched(arg=True)调用时,装饰器传入的mock对象会被赋值给第一个参数arg,而你又通过关键字参数给arg传了值,导致同一个参数被重复赋值,触发错误。
解决方案
方案1:调整参数顺序,将mock参数放在最前面
把mock参数移到函数参数列表的首位,装饰器注入的mock会被正确赋值给对应参数,业务参数用关键字传递不会冲突:
import traceback from unittest.mock import patch import os.path @patch('os.path.isfile') def being_patched(isfile_mock, arg): print(f"Inside being_patched. arg={arg} isfile_mock={isfile_mock}") if __name__ == '__main__': being_patched(True) # 仍可正常运行 print("\n----------\n") being_patched(arg=True) # 现在可以正常运行
方案2:使用关键字-only参数接收mock
通过*将mock参数设为关键字-only,同时给@patch指定kwarg参数,让装饰器以关键字参数形式注入mock:
import traceback from unittest.mock import patch import os.path @patch('os.path.isfile', kwarg='isfile_mock') def being_patched(arg, *, isfile_mock): print(f"Inside being_patched. arg={arg} isfile_mock={isfile_mock}") if __name__ == '__main__': being_patched(True) # 正常运行 print("\n----------\n") being_patched(arg=True) # 正常运行
方案3:混合位置与关键字参数调用(不推荐)
若不想修改函数定义,可在调用时先传占位的位置参数,再传关键字参数,但可读性差,不建议使用:
# 原函数定义不变 being_patched(None, arg=True) # None为占位,实际会被装饰器替换为真实mock对象
内容的提问来源于stack exchange,提问作者Mike Ulm
相关产品推荐
相关产品推荐

