装饰器注释
Annotations for decorator
我正在努力注释装饰器函数。
执行 mypy --strict .
mypy 时抱怨:test.py:25: error: Untyped decorator makes function "func1" untyped
.
据我所知,我的装饰器不是无类型的...
我可以更改的是装饰器的注释,我不能更改对象生成方式(类生成器和测试)或参数传递方式(实例化)背后的概念).
from typing import Dict, Any, List
import functools
def has_params(params: Dict[str, Any]) -> Any:
def param_decorator(func: Any) -> Any:
@functools.wraps(func)
def func_decorator(*args: Any) -> Any:
class_obj = args[0]
for param in params:
if (param not in class_obj.params.keys()):
raise Exception(f'Parameter {param} not passed to object, but required by function {class_obj.name}.{func.__name__}')
if (not isinstance(class_obj.params[param], params[param])):
raise Exception(f'Parameter {param} for object {class_obj.name} has the wrong type, expected {params[param]}, got {type(class_obj.params[param])}')
return func(*args)
return func_decorator
return param_decorator
class Test():
def __init__(self) -> None:
self.__params: Dict[str, Any] = dict()
self.__name = "Test"
@has_params(params={'param1': bool})
def func1(self) -> None:
# self.params['param1'] can be safely accessed here because it is type checked by @has_params
print(f'Got param1: {self.params["param1"]}')
@property
def params(self) -> Dict[str, Any]:
return self.__params
@params.setter
def params(self, value: Dict[str, Any]) -> None:
self.__params = value
@property
def name(self) -> str:
return self.__name
class Generator():
def __init__(self) -> None:
self.__obj_list: List[Test] = list()
def add_elem(self, name: str, params: Dict[str, Any] = {}) -> None:
obj = self._create_object_by_name(name)
obj.params = params
self.__obj_list.append(obj)
def execute(self) -> None:
for obj in self.__obj_list:
obj.func1()
def _create_object_by_name(self, class_name: str) -> Any:
if class_name in globals():
return globals()[class_name]()
else:
raise Exception(f'Class "{class_name}" is not defined')
gen = Generator()
gen.add_elem('Test', params={'param1': 7.5})
gen.execute()
我认为问题在于装饰器上的注释 return Any
,而实际上它应该 return 一个 Callable
。如果您将 has_params
尤其是 param_decorator
的 return 类型更改为 Callable[..., Any]
,它会起作用。
或者更好的是,声明一个具有任意可调用对象上限的类型变量:
F = TypeVar('F', bound=Callable[..., Any])
并注释
def param_decorator(func: F) -> F: ...
更新: 自己测试时,我还必须添加:
return cast(F, func_decorator)
在 param_decorator
的末尾,否则 mypy 抱怨
Incompatible return value type (got "Callable[[VarArg(Any)], Any]", expected "F")
我不确定是否有更好的方法来处理通用函数包装,尽管这似乎与 mypy 文档的建议一致。
另见 https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
我正在努力注释装饰器函数。
执行 mypy --strict .
mypy 时抱怨:test.py:25: error: Untyped decorator makes function "func1" untyped
.
据我所知,我的装饰器不是无类型的...
我可以更改的是装饰器的注释,我不能更改对象生成方式(类生成器和测试)或参数传递方式(实例化)背后的概念).
from typing import Dict, Any, List
import functools
def has_params(params: Dict[str, Any]) -> Any:
def param_decorator(func: Any) -> Any:
@functools.wraps(func)
def func_decorator(*args: Any) -> Any:
class_obj = args[0]
for param in params:
if (param not in class_obj.params.keys()):
raise Exception(f'Parameter {param} not passed to object, but required by function {class_obj.name}.{func.__name__}')
if (not isinstance(class_obj.params[param], params[param])):
raise Exception(f'Parameter {param} for object {class_obj.name} has the wrong type, expected {params[param]}, got {type(class_obj.params[param])}')
return func(*args)
return func_decorator
return param_decorator
class Test():
def __init__(self) -> None:
self.__params: Dict[str, Any] = dict()
self.__name = "Test"
@has_params(params={'param1': bool})
def func1(self) -> None:
# self.params['param1'] can be safely accessed here because it is type checked by @has_params
print(f'Got param1: {self.params["param1"]}')
@property
def params(self) -> Dict[str, Any]:
return self.__params
@params.setter
def params(self, value: Dict[str, Any]) -> None:
self.__params = value
@property
def name(self) -> str:
return self.__name
class Generator():
def __init__(self) -> None:
self.__obj_list: List[Test] = list()
def add_elem(self, name: str, params: Dict[str, Any] = {}) -> None:
obj = self._create_object_by_name(name)
obj.params = params
self.__obj_list.append(obj)
def execute(self) -> None:
for obj in self.__obj_list:
obj.func1()
def _create_object_by_name(self, class_name: str) -> Any:
if class_name in globals():
return globals()[class_name]()
else:
raise Exception(f'Class "{class_name}" is not defined')
gen = Generator()
gen.add_elem('Test', params={'param1': 7.5})
gen.execute()
我认为问题在于装饰器上的注释 return Any
,而实际上它应该 return 一个 Callable
。如果您将 has_params
尤其是 param_decorator
的 return 类型更改为 Callable[..., Any]
,它会起作用。
或者更好的是,声明一个具有任意可调用对象上限的类型变量:
F = TypeVar('F', bound=Callable[..., Any])
并注释
def param_decorator(func: F) -> F: ...
更新: 自己测试时,我还必须添加:
return cast(F, func_decorator)
在 param_decorator
的末尾,否则 mypy 抱怨
Incompatible return value type (got "Callable[[VarArg(Any)], Any]", expected "F")
我不确定是否有更好的方法来处理通用函数包装,尽管这似乎与 mypy 文档的建议一致。
另见 https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators