如何解决来自pyright的“类型部分未知”警告?

Spe*_*pen 4 python static-typing typing python-3.x pyright

我正在通过 pyright 使用严格的类型检查。

当我有一个返回 pytorch 的方法时,DataLoaderpyright 会抱怨我的类型定义:

声明的返回类型“DataLoader[Unknown]”部分未知 Pyright (reportUnknownVariableType)

看一下 pytorch 的类型存根DataLoader(减少到重要部分):

class DataLoader(Generic[T_co]):
    dataset: Dataset[T_co]

    @overload
    def __init__(self, dataset: Dataset[T_co], ...
Run Code Online (Sandbox Code Playgroud)

据我所看到的,通用型T_coDataLoader应该由定义__init__数据集参数。

Pyright 还抱怨我的Dataset类型定义:

参数“数据集”的类型部分未知参数类型为“数据集[未知]”Pyright (reportUnknownParameterType)

看一下Dataset类型存根:

class Dataset(Generic[T_co]):
    def __getitem__(self, index: int) -> T_co: ...
Run Code Online (Sandbox Code Playgroud)

向我表明该类型应该由__getitem__.

我的数据集的类型签名__getitem__如下所示:

def __getitem__(self, index: int) -> Tuple[Tensor, Tensor]:
Run Code Online (Sandbox Code Playgroud)

基于此,我希望Dataset并被DataLoader推断为Dataset[Tuple[Tensor, Tensor]]DataLoader[Tuple[Tensor, Tensor]]但事实并非如此。

我的猜测是 pyright 无法静态推断这里的类型。

我以为我可以像这样定义我自己的类型签名:

Dataset[Tuple[Tensor, Tensor]]
Run Code Online (Sandbox Code Playgroud)

但这实际上导致我的 python 脚本崩溃:

类型错误:“类型”对象不可下标

我怎样才能正确地定义类型DatasetDataLoader

Spe*_*pen 6

由于没有对这个问题的答复,我不确定它是否真的是 pyright 中的错误。因此,我在 github 存储库上打开了这个问题:https : //github.com/microsoft/pyright/issues/698

Eric Traut 详细解释了问题所在以及 pyright 正在按设计工作。我试着在这里给出要点的要点。

问题说明

如果没有提供返回类型,Pyright 会尝试推断返回类型,但如果在这种情况下提供了返回类型,则需要完全输入它们。Pyright 不会填充给定类型注释的缺失部分。

例如,pyright 将尝试推断以下函数定义的返回类型:

def get_dataset():
Run Code Online (Sandbox Code Playgroud)

但是如果返回类型是这样给出的,Dataset那么这就是 pyright 所期望的返回类型。

def get_dataset() -> Dataset:
Run Code Online (Sandbox Code Playgroud)

在这种情况下Dataset是一个通用类,它不处理像Dataset[int]. 在 Python 3.7(我们正在使用的)中,Python 解释器将评估导致上述异常的这些类型注释。

解决方案

从 Python 3.10 开始,Python 解释器将不再评估类型注释,以下类型注释将正常工作:

def get_dataset() -> Dataset[int]:
Run Code Online (Sandbox Code Playgroud)

从 Python 3.7 开始,可以通过以下导入启用此行为:

from __future__ import annotations
Run Code Online (Sandbox Code Playgroud)

这记录在PEP 563 中。您还需要禁用规则 E1136,以便 pylint 不警告“不可订阅对象”。

另一种解决方法是引用这样的类型定义:

def get_dataset() -> "Dataset[int]":
Run Code Online (Sandbox Code Playgroud)