为什么 mypy/PyCharm/etc 没有检测到 Type[T] 的类型错误?

Yat*_*wal 0 python typing pycharm mypy python-typing

考虑以下代码:

def verify(schema: Type[T], data: T) -> None:
    pass

verify(int, "3")
verify(float, "3")
verify(str, "3")
Run Code Online (Sandbox Code Playgroud)

我希望前两个verify()调用显示为类型错误,而最后一个则不会。

然而,在 PyCharm 和 mypy 中,它们都没有出现类型错误。我尝试启用所有可能的严格性标志和错误代码,但什么也没有。

我怎样才能让类型检查器对此进行类型检查?为什么会失败?

库喜欢apischema依赖这样的功能来进行类型检查,例如apischema.serialize(MyDataclass, my_dataclass),但这也不起作用。

che*_*ner 5

绑定到的类型T不是由任何单个参数确定的;它是以满足召唤的方式选择的。由于是-and-或-and- )object最接近的常见超类型,因此在这些情况下必然是。(对于,必然出于同样的原因:它是和的最简单的最接近的超类型)。intstrfloatstrTobjectverify(str, "3")Tstrstrstr

您可以通过verifyreturn来看到这一点schema,然后询问mypy返回值是什么。

def verify(schema: Type[T], data: T) -> Type[T]:
    return schema

reveal_type(verify(int, "3"))  # builtins.object
reveal_type(verify(float, "3"))  # builtins.object
reveal_type(verify(str, "3"))  # builtins.str
Run Code Online (Sandbox Code Playgroud)

如果您可以枚举您期望传递给的类型,则可以通过创建约束schema类型变量来部分解决问题。T

T = TypeVar('T', int, str, float)

def verify(schema: Type[T], data: T) -> None:
    ...
Run Code Online (Sandbox Code Playgroud)

由于T无法再绑定到object,因此您的前两个示例将无法根据需要进行类型检查。


请注意,类似以下内容仍然会通过:

class MyFloat(float):
    pass


verify(MyFloat, 3.14)
Run Code Online (Sandbox Code Playgroud)

因为T将绑定到和float的最接近的公共超类。(这实际上与 的情况没有什么不同,但往往“看起来”不那么令人惊讶。)floatMyFloatobject

  • 值得注意的是,“int”+“str”->“object”的这种行为并不是_必要_的,而是任何给定类型检查器做出的选择。MyPy 选择在这些类型上使用 **join**,就像在许多类似实例中一样(这种行为[相当有争议](https://github.com/python/mypy/issues/12056))并且加入是你在这里描述的。但处理这个问题的另一种方法是使用 **union**,即“T”折叠为“int |”。斯特`。我相信 Pyright 可以做到这一点,尽管我还没有测试过。我不知道PyCharm的内置检查器是做什么的。 (2认同)