3

我有一个函数,它接受一个派生自NamedTuple并将其转换为模式的类。但是,当我在以下代码上运行 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 文档声明接受(https://docs.python.org/3/library/typing.html#typing.TypeType[Foo]的任何子类。对于我们的数据模型中的实体,我有多个 , 子类,所以我正在寻找一种方法来以一种可以进行类型检查的方式注释函数。FooNamedTuple

4

1 回答 1

1

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

这意味着它NamedTuple实际上根本不是一个可以使用的有效类型——在这方面,mypy 只是默默地让你Type[NamedTuple]开始构造,这让我有点惊讶。

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

  1. 与其使用Type[NamedTuple],不如使用Type[tuple]Type[Tuple[Any]]

    毕竟,您Foo真的是元组的子类型。

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

    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第三方库才能使用它,尽管有计划在某个时候将协议形式化并将其添加到 Python 中。(我忘记了计划是 Python 3.7 还是 3.8)。

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

相关讨论见this issue。基本上,mypy 团队的共识是,有人应该对这个 NamedTuple 事情做点什么,无论是通过添加错误消息还是通过添加官方认可的协议,但我认为人们太忙于其他任务/错误,无法推动这一进程. (因此,如果您感到无聊并正在寻找可以做的事情...)

于 2018-07-25T17:09:33.550 回答