警告原因
当调用装饰器装饰的实例方法时,PyCharm 会警告 Parameter 'self' unfilled
;这可能是因为 PyCharm 未将装饰器装饰的实例方法视作实例方法,导致误判。我认为这是一个 PyCharm 的一个 Bug,可以在官网的 Bug 反馈中反馈这个问题:https://youtrack.jetbrains.com/issues
警告样例
def once_per_instance(method):
"""保证每个实例中方法只能被调用一次的装饰器"""
@wraps(method)
def wrapper(self, *args, **kwargs):
# 在实例中,增加 _already_visited_instances 属性,用于统计哪些方法已被调用
if "_already_visited_instances" not in self.__dict__:
self.__dict__["_already_visited_instances"] = set()
if method.__name__ not in self.__dict__["_already_visited_instances"]:
self.__dict__["_already_visited_instances"].add(method.__name__)
return method(self, *args, **kwargs)
raise ValueError(f"instance method {method.__name__} can only be called once")
return wrapper
if __name__ == "__main__":
class TestClass:
@once_per_instance
def test_method(self):
pass
test_instance = TestClass()
test_instance.test_method() # 这里警告: Parameter 'self' unfilled
test_instance.test_method() # 这里警告: Parameter 'self' unfilled
解决方法 1(推荐):使用 PEP-484 规范,标注返回值类型,将装饰器返回值标记为 Callable
类型
def once_per_instance(method: Callable) -> Callable:
"""保证每个实例中方法只能被调用一次的装饰器"""
@wraps(method)
def wrapper(self, *args, **kwargs):
# 在实例中,增加 _already_visited_instances 属性,用于统计哪些方法已被调用
if "_already_visited_instances" not in self.__dict__:
self.__dict__["_already_visited_instances"] = set()
if method.__name__ not in self.__dict__["_already_visited_instances"]:
self.__dict__["_already_visited_instances"].add(method.__name__)
return method(self, *args, **kwargs)
raise ValueError(f"instance method {method.__name__} can only be called once")
return wrapper
解决方法 2(推荐):添加 docstring,将装饰器返回值标记为 Callable
类型
def once_per_instance(method: Callable) -> Callable:
"""保证每个实例中方法只能被调用一次的装饰器
Parameters
----------
method : Callable
待装饰的实例方法
Returns
-------
wrapper : Callable
被装饰的实例方法
"""
@wraps(method)
def wrapper(self, *args, **kwargs):
# 在实例中,增加 _already_visited_instances 属性,用于统计哪些方法已被调用
if "_already_visited_instances" not in self.__dict__:
self.__dict__["_already_visited_instances"] = set()
if method.__name__ not in self.__dict__["_already_visited_instances"]:
self.__dict__["_already_visited_instances"].add(method.__name__)
return method(self, *args, **kwargs)
raise ValueError(f"instance method {method.__name__} can only be called once")
return wrapper
解决方法 3(不推荐):删掉 self
形参,改为使用 args[0]
这个严重违反 Python 社区规范,非常不推荐。
def once_per_instance(method):
"""保证每个实例中方法只能被调用一次的装饰器"""
@wraps(method)
def wrapper(*args, **kwargs):
self = args[0]
# 在实例中,增加 _already_visited_instances 属性,用于统计哪些方法已被调用
if "_already_visited_instances" not in self.__dict__:
self.__dict__["_already_visited_instances"] = set()
if method.__name__ not in self.__dict__["_already_visited_instances"]:
self.__dict__["_already_visited_instances"].add(method.__name__)
return method(*args, **kwargs)
raise ValueError(f"instance method {method.__name__} can only be called once")
return wrapper