Mypy 不使用 Type[NamedTuple] 进行类型检查

Mypy doesn't typecheck function with Type[NamedTuple]

我有一个函数接受从 NamedTuple 派生的 class 并将其转换为模式。然而,当我 运行 MyPy 在下面的代码上它失败 Argument 1 to "to_schema" has incompatible type "Type[Foo]"; expected "Type[NamedTuple]"

from typing import NamedTuple, Type


def to_schema(named_tuple: Type[NamedTuple]):
    pass

class Foo(NamedTuple):
    pass


to_schema(Foo) 

有没有一种方法可以正确输入代码,以便使用 MyPy 进行类型检查?

编辑: Python 文档指出 Type[Foo] 接受 Foo (https://docs.python.org/3/library/typing.html#typing.Type) 的任何子 class。对于我们的数据模型中的实体,我有 NamedTuple 的多个子 classes,所以我正在寻找一种方法来以类型检查的方式注释函数。

您的代码的根本问题是 NamedTuple 不是实际类型——它实际上只是一种特殊类型的 "type constructor",它综合了一个全新的 class 和类型。例如。如果您尝试打印出 Foo.__mro__ 的值,您会看到 (<class '__main__.Foo'>, <class 'tuple'>, <class 'object'>) —— NamedTuple 根本不存在。

这意味着 NamedTuple 实际上根本不是一个有效的类型——在这方面,mypy 只是默默地让你构造 Type[NamedTuple]开始于.

要解决此问题,您有几种可能的方法:

  1. 而不是使用 Type[NamedTuple],使用 Type[tuple]Type[Tuple[Any]]

    你的Foo毕竟元组的子类型。

  2. 如果您需要专门仅存在于命名元组中的方法或字段,请使用自定义协议。例如,如果你特别需要 namedtuples 中的 _asdict 方法,你可以这样做:

    from typing_extensions import Protocol
    
    class NamedTupleProto(Protocol):
        def _asdict(self) -> Dict[str, Any]: ...
    
    def to_schema(x: Type[NamedTupleProto]) -> None: pass
    
    class Foo(NamedTuple):
        pass
    
    to_schema(Foo)
    

    请注意,您需要安装 typing_extensions 第三方库才能使用它,尽管有计划 formalize Protocols 并将其添加到 Python 中。 (我忘了计划是 Python 3.7 还是 3.8)。

  3. 在对 to_schema 的调用中添加类型忽略或强制转换以使 mypy 静音。这不是最好的解决方案,但也是最快的。

相关讨论见this issue。基本上,mypy 团队一致认为有人应该对这个 NamedTuple 事情做些什么,无论是通过添加错误消息还是通过添加官方认可的协议,但我认为人们忙于其他 tasks/bugs 以推动这个前进。 (所以如果你无聊想找点事做...)