Mypy 抱怨 __setitem__ 签名

问题描述

我有一个 list 的子类,它在列表的组件上添加了一些管理内容。一切正常,但 mypy 抱怨 super 调用 __setitem__ 的签名。 这是问题减少到最低限度:

from typing import List,Iterable,Union,overload
from typing_extensions import SupportsIndex


class MyData:
    pass


class MyDataSeq(List[MyData]):
    @overload
    def __setitem__(self,index: SupportsIndex,value: MyData) -> None: ...

    @overload
    def __setitem__(self,index: slice,value: Iterable[MyData]) -> None: ...

    def __setitem__(self,index: Union[SupportsIndex,slice],value: Union[MyData,Iterable[MyData]]) -> None:
        # Administrative stuff deleted
        super().__setitem__(index,value)

    def __delitem__(self,slice]) -> None:
        # Administrative stuff deleted
        super().__delitem__(index)

当我运行 mypy 时,我得到:

src\seq.py:18: error: Invalid index type "Union[SupportsIndex,slice]" for "MyDataSeq"; expected type "SupportsIndex"
src\seq.py:18: error: Incompatible types in assignment (expression has type "Union[MyData,Iterable[MyData]]",target has type "MyData")
Found 2 errors in 1 file (checked 1 source file)

在这里不知所措,因为显然 __setitem____delitem__ 一样,同时接受 int-like (SupportsIndex) 和 {{1}对象作为它的第一个参数。几乎好像 slice 以某种方式得出了只支持 mypy 类对象的结论——这与第二个错误相匹配,即它只期望一个 int 作为第二个参数,而不是 { {1}}。

我在 Python 3.7 和 3.9 上都试过这个,错误是一样的。 我当然可以告诉 MyData 忽略这些错误,但我真的很想知道这是什么原因造成的,以及如何解决它。有什么想法吗?

解决方法

根据this类似的问题。 Mypy 不使用重载信息对函数体进行类型检查。

要解决您的问题,您可以通过 isinstance 向 Mypy give a hint 传递您传递给它的类型。像这样:

from typing import List,Iterable,Union,overload
from typing_extensions import SupportsIndex


class MyData:
    pass


class MyDataSeq(List[MyData]):
    @overload
    def __setitem__(self,index: SupportsIndex,value: MyData) -> None: ...

    @overload
    def __setitem__(self,index: slice,value: Iterable[MyData]) -> None: ...

    def __setitem__(self,index: Union[SupportsIndex,slice],value: Union[MyData,Iterable[MyData]]) -> None:
        # Administrative stuff deleted
        if isinstance(index,slice) and isinstance(value,Iterable):
            super().__setitem__(index,value)
        elif isinstance(index,int) and isinstance(value,MyData):
            super().__setitem__(index,value)
        else:
            raise TypeError(f"{index}/{value} Invalid index/value type.")

    def __delitem__(self,slice]) -> None:
        # Administrative stuff deleted
        super().__delitem__(index)
,

您不需要对实现进行注释,只需对修饰的定义进行注释。

class MyDataSeq(List[MyData]):
    @overload
    def __setitem__(self,value: MyData) -> None:
        ...

    @overload
    def __setitem__(self,value: Iterable[MyData]) -> None:
       ...

    def __setitem__(self,index,value):
        # Administrative stuff deleted
        super().__setitem__(index,value)

    def __delitem__(self,slice]) -> None:
        # Administrative stuff deleted
        super().__delitem__(index)

(这会给 __delitem__ 留下一个类似的问题,但我认为这是一个单独的问题。)