python - 装饰器的注解

标签 python type-hinting mypy python-typing

这个问题在这里已经有了答案:





MyPy Errors for Decorator w/ Arguments

(1 个回答)


10 天前关闭。




我正在努力注释装饰器函数。
执行 mypy --strict . 时mypy 提示:test.py:25: error: Untyped decorator makes function "func1" untyped .
据我所知,我的装饰者不是无类型的......
我可以改变的是装饰器的注释,我不能 改变对象生成方式(Generator 和 Test 类)或参数传递方式(实例化)背后的概念。

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()

最佳答案

我相信问题在于您的装饰器上的注释返回 Any , 当真的它应该返回 Callable .如果您更改 has_params 的返回类型尤其是 param_decoratorCallable[..., 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

关于python - 装饰器的注解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69361179/

相关文章:

python - 重写方法签名时如何避免违反里氏替换原则

python - "Union[int, str]"的索引类型 "Union[List[Any], Dict[Any, Any]]"无效;预期类型 "int"

python - mypy 和嵌套的 dict 理解

python - 如何在 matplotlib 图形中添加新颜色(或使用颜色图)?

python - 如何通过 python 3.5 输入模块输入提示 collections.OrderedDict

data-structures - 如何实现递归deftype

python - 在旧版 Python 中创建带有可变参数的 Typing.Annotated 实例

python - OpenCV Python,从命名管道读取视频

python - Cython - 将指向数组的指针转换为 Python 对象

python - 如何使用 python hug 获取请求 header