python - 签名中带有 TypeVar 的协议(protocol)的实现者不能使用自己的类型

标签 python protocols mypy python-typing type-variables

我试图为我的协议(protocol)的用户提供一种方法,可以使用他们想要的任何内容作为某些任意数据结构的键和值的类型。

通常它是一个 Dict,但它也可以很容易地是一些外部数据存储,比如 Redis。

我尝试使用 Protocol 向用户提供此选项,但 mypy 不允许实现者在方法签名中使用他们自定义的类型。

有没有办法修复这种打字问题,还是我根本上使用了错误的工具?

我使用的是 Python 3.11,脚本如下:

from typing import (
    Dict,
    Generic,
    Optional,
    Protocol,
    Type,
    TypeVar,
    Union,
    runtime_checkable,
)

Key = TypeVar("Key")
Value = TypeVar("Value")


@runtime_checkable
class Data(Protocol):
    async def get_value(self, key: Key) -> Optional[Value]:
        ...

    async def set_value(self, key: Key, value: Value) -> None:
        ...


class DataUser(Generic[Key, Value]):
    def __init__(
        self,
        cls_data: Type[Data],
    ) -> None:
        self.__data = cls_data()

    async def get_value(self, key: Key) -> Optional[Value]:
        return await self.__data.get_value(key)

    async def set_value(self, key: Key, value: Value) -> None:
        await self.__data.set_value(key=key, value=value)


KVDataValue = Union[str, int, float]


class KVData:
    def __init__(self) -> None:
        self.__data: Dict[str, KVDataValue] = {}

    async def get_value(self, key: str) -> Optional[KVDataValue]:
        return self.__data.get(key)

    async def set_value(self, key: str, value: KVDataValue) -> None:
        self.__data[key] = value


data_user = DataUser[str, KVDataValue](
    cls_data=KVData,
)

在此代码上运行 mypy 会产生:

src/aiomon/example.py:57:14: error: Argument "cls_data" to "DataUser" has incompatible type "Type[KVData]"; expected "Type[Data]"  [arg-type]
        cls_data=KVData,
                 ^~~~~~
Found 1 error in 1 file (checked 11 source files)

值得注意的是,我使用 PyCharm 作为我的 IDE,它表明这是正确的键入。此外,例如,当我从 KVData 中删除 set_value 方法时,它正确地识别出此类停止遵守 Data 协议(protocol)。

最佳答案

如果协议(protocol)在其方法接受(和返回)参数方面应该是通用的,那么您应该这样定义它。 (参见"Generic Protocols" in PEP 544)

毕竟,DataUser 已经是通用的,并且(如果我正确理解了您的用例)它的类型参数应该绑定(bind)到底层 Data 类的类型参数。

这似乎工作得很好:

from typing import Generic, Optional, Protocol, TypeVar, Union, runtime_checkable

K = TypeVar("K", contravariant=True)
V = TypeVar("V")


@runtime_checkable
class Data(Protocol[K, V]):
    async def get_value(self, key: K) -> Optional[V]: ...

    async def set_value(self, key: K, value: V) -> None: ...


class DataUser(Generic[K, V]):
    def __init__(self, cls_data: type[Data[K, V]]) -> None:
        self.__data = cls_data()

    async def get_value(self, key: K) -> Optional[V]:
        return await self.__data.get_value(key)

    async def set_value(self, key: K, value: V) -> None:
        await self.__data.set_value(key=key, value=value)


KVDataValue = Union[str, int, float]


class KVData:
    def __init__(self) -> None:
        self.__data: dict[str, KVDataValue] = {}

    async def get_value(self, key: str) -> Optional[KVDataValue]:
        return self.__data.get(key)

    async def set_value(self, key: str, value: KVDataValue) -> None:
        self.__data[key] = value


data_user = DataUser[str, KVDataValue](cls_data=KVData)

顺利通过mypy --strict

关于python - 签名中带有 TypeVar 的协议(protocol)的实现者不能使用自己的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76501748/

相关文章:

python - Heroku 的 Gunicorn 和 Bottle 设置

python - 聚合行 Pandas

Swift 委托(delegate)协议(protocol)未调用

security - 一个简单的、中等安全的登录协议(protocol)?

javascript - JavaScript 书签是否必须是有效的 URL?

python - 如何确定 python 中任何生成器的产量/发送/返回值

javascript - 模板无法通过 flask 正确渲染

python - 通过 Python 替换文件中的更新版本字符串

python - 使用 typing.Literal 的正确方法是什么?

python - 如何注释将 AnyStr 设为 str 默认值的函数?