我试图为我的协议(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/