mypy:“__setitem__”的签名与超类型“list”不兼容

问题描述

我正在尝试使用 List 的一个专门子类来创建一个由常规列表支持的 TokenList,但还包含一个带有键/值的元数据字典。一切正常,直到我想为此添加类型,现在我收到此错误

error: Signature of "__setitem__" incompatible with supertype "list"

... 错误是下面的第一个重载:

import typing as T

class Token(dict):
    ...

class TokenList(T.List[Token]):
    ...

    @T.overload
    def __setitem__(self,key: int,tokens: T.Union[dict,Token]) -> None: ...  # noqa,pragma: no cover

    @T.overload
    def __setitem__(self,key: slice,tokens: T.Union[T.Iterable[T.Union[dict,Token]],'TokenList']) -> None: ...  # noqa,pragma: no cover

    def __setitem__(self,key,tokens):  # noqa: F811
        if isinstance(key,int):
            token = tokens
            token = self._dict_to_token_and_set_defaults(token)
            super(TokenList,self).__setitem__(key,token)
        else:
            tokens = [self._dict_to_token_and_set_defaults(token) for token in tokens]
            super(TokenList,tokens)

我试图找到 list 的类型定义以将其与我的进行比较,但一直找不到。

除了忽略这个错误继续我的生活,还有什么办法可以解决这个问题?任何指点表示赞赏。

(如果重要的话,我使用的是 mypy 0.910 和 Python 3.9.1。)

解决方法

您可以使用 reveal_type 找出任何对象的静态类型。所以尝试:

reveal_type(list.__setitem__)

给你:

reveal_type.py:2: note: Revealed type is "Overload(def [_T] (builtins.list[_T`1],typing_extensions.SupportsIndex,_T`1),def [_T] (builtins.list[_T`1],builtins.slice,typing.Iterable[_T`1]))"

您的原始代码的问题在于,列表可以被不仅仅是 intslice 索引:它们支持任何可以使用 __index__ 强制转换为整数的对象作为嗯。

这意味着您需要进行两项更改:导入 typing_extensions 并将 int 替换为 typing_extensions.SupportsIndex

import typing_extensions as TE

...

def __setitem__(self,key: TE.SupportsIndex,tokens: T.Union[dict,Token]) -> None: ...  # noqa,pragma: no cover

此外,这意味着您的实现不太正确,因为非整数非切片索引将被视为切片索引。我会这样写:

def __setitem__(self,key,tokens):  # noqa: F811
    if isinstance(key,slice):
        tokens = [self._dict_to_token_and_set_defaults(token) for token in tokens]
    else:
        tokens = self._dict_to_token_and_set_defaults(tokens)
    super().__setitem__(key,tokens)