用类编写Python装饰器时PyCharm提示Unexpected argument是其bug吗
问题描述
我在使用类编写带参数的Python装饰器时遇到问题,对应异常提示截图如下:
复现代码
class Delay: def __init__(self, sec): self.sec = sec def __call__(self, func): self.func = func return self.wrapper def wrapper(self, *args, **kwargs): import time time.sleep(self.sec) result = self.func(*args, **kwargs) return result @Delay(2) def myfunc(n): return n * 2 if __name__ == '__main__': print(myfunc(6))
上述代码实际运行可正常输出结果,但PyCharm在myfunc(6)调用位置提示Unexpected argument错误,请问该提示是否为PyCharm本身的bug导致?
解答
这个提示不是PyCharm的bug,属于静态检查对当前写法的签名推断偏差,同时你写的装饰器本身也存在潜在问题:
- 触发提示的原因:
__call__返回的是绑定到Delay实例的self.wrapper方法,PyCharm做静态分析时,会把这个绑定方法识别为无额外显式入参的方法(实例方法的self参数在绑定时已经隐式传入,静态检查没识别到方法内部定义的*args, **kwargs可变参数),因此判定你传入参数6是多余的。 - 额外隐患:当前写法如果用同一个Delay实例装饰多个函数,
self.func属性会被最后一个被装饰的函数覆盖,之前装饰的函数执行时会调用错误的逻辑。
最稳妥的修复方式是把包装函数改成__call__内部的嵌套函数,同时用functools.wraps保留原函数元信息,改完之后PyCharm可以正确识别函数签名,也能解决多函数装饰的覆盖问题:
import time from functools import wraps class Delay: def __init__(self, sec): self.sec = sec def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): time.sleep(self.sec) return func(*args, **kwargs) return wrapper @Delay(2) def myfunc(n): return n * 2 if __name__ == '__main__': print(myfunc(6))
如果暂时不想调整代码结构,也可以通过给wrapper补充类型注解的方式让PyCharm识别参数,但这种方式解决不了func被覆盖的问题,不推荐在生产环境使用。
内容的提问来源于stack exchange,提问作者LZZ
相关产品推荐
相关产品推荐

