我有以下(精简的)数据类:
from dataclasses import dataclass
from typing import Union, Type
class BaseType: ...
class PathType(BaseType): ...
class DataType(BaseType): ...
_FinalTypes = Union[PathType, DataType]
@dataclass
class InterfaceInfo:
what: Type[_FinalTypes]
name: str
def __call__(self, *args, **kwargs) -> _FinalTypes:
return self.what(*args, **kwargs)
print(InterfaceInfo(PathType, "path"))
print(InterfaceInfo(DataType, "path"))
但我不确定如何正确注释它。 我的目的实际上是,无论您传入
__init__
方法中的任何类型,都应该作为物化对象从 __call__
中出来。
因为我现在写的内容,类型检查器会认为可以使用 PathType 构造一个
InterfaceInfo
,并从中产生一个 DataType 对象。
如果这是一个方法,我可以使用
@overload
来输入提示,但这是一个类,所以我不知所措......
我研究了绑定到 BaseType 的 TypeVar。但那不是同样可能吗?或者类型检查器是否足够聪明,可以知道 Type[PathType] 进来,并且 PathType 需要出来?
如何解决这个问题?
谢谢!
在某种程度上,可以通过根据
InterfaceInfo
类型将 what
定义为 generic 来解决这个问题。所以我想说你关于使用类型变量的想法是正确的。
假设以下具体
BaseType
子类型:
from dataclasses import dataclass
@dataclass
class BaseType:
pass
@dataclass
class PathType(BaseType):
path: str
@dataclass
class DataType(BaseType):
data: bytes
我们用
BaseType
的上限定义类型变量,然后用该类型变量注释 what
以及 __call__
返回,如下所示:
# ... import BaseType, PathType, DataType
from typing import Any, Generic, TypeVar
_T = TypeVar("_T", bound=BaseType)
@dataclass
class InterfaceInfo(Generic[_T]):
what: type[_T]
name: str
def __call__(self, *args: Any, **kwargs: Any) -> _T:
return self.what(*args, **kwargs)
用途:
path_interface = InterfaceInfo(PathType, "foo")
data_interface = InterfaceInfo(DataType, "bar")
p = path_interface(path="spam/eggs")
d = data_interface(data=b"ff00")
print(p)
print(d)
# for mypy:
reveal_type(p)
reveal_type(d)
print
调用输出:
PathType(path='spam/eggs')
DataType(data=b'ff00')
mypy
输出:
note: Revealed type is "PathType"
note: Revealed type is "DataType"
我们想要和期望什么。
我首先说“在某种程度上”的原因是,
__call__
方法的签名显然只是一种廉价的解决方法。静态类型检查器无法验证我们在调用 path_interface
或 data_interface
时传递的参数是否与 what
的类型兼容。
我想了一会儿,但无法想出一个类型安全的解决方案来允许将
__call__
注释为通用到 that 程度。这意味着返回类型可能是安全的,但构造函数参数不安全。也许其他人知道一种方法,或者也许 Python 类型系统不适合这个。