在Python中使用适当的类型提示对类序列进行子类化

Ily*_*rov 3 python typechecking mypy

我正在尝试在Python中实现一种自定义序列类:

from typing import Sequence, TypeVar, List

T = TypeVar('T')

class MySequence(Sequence[T]):
    def __init__(self):
        self._container: Sequence[T] = []
    def __getitem__(self, idx):
        return self._container[idx]
    def __len__(self):
        return len(self._container)
Run Code Online (Sandbox Code Playgroud)

现在我想检查mypy是否知道MySequence类型的项目的元素T:

foo: MySequence[str] = MySequence()
reveal_type(foo[0])
# Revealed type is 'Any'
Run Code Online (Sandbox Code Playgroud)

所以它失败了:mypy一无所知foo.普通Sequence作品的相同例子:

bar: Sequence[str] = []
reveal_type(bar[0])
# Revealed type is 'builtins.str*'
Run Code Online (Sandbox Code Playgroud)

如果我正在尝试向实现添加类型注释__getitem__,我还有另一个错误:

def __getitem__(self, idx) -> T:
# Signature of "__getitem__" incompatible with supertype "Sequence"
Run Code Online (Sandbox Code Playgroud)

我也试过了

def __getitem__(self, idx) -> Union[T, Sequence[T]]:
Run Code Online (Sandbox Code Playgroud)

作为idx可切片在那种情况下我的代码将返回一个序列,而不是一个元素.它使用相同的消息失败.

正如上一个问题中所讨论的那样,对这类问题进行公开讨论.

但是,我仍然想知道,是否有可能创建自定义序列类型,允许mypy提取有关其项目类型的信息,如我的示例中所示?

Mic*_*x2a 5

在这种情况下,正确的做法是正确覆盖确切的签名__getitem__,包括重载.

from typing import Sequence, TypeVar, List, overload, Union

T = TypeVar('T', covariant=True)

class MySequence(Sequence[T]):
    def __init__(self):
        self._container: Sequence[T] = []

    @overload
    def __getitem__(self, idx: int) -> T: ...

    @overload
    def __getitem__(self, s: slice) -> Sequence[T]: ...

    def __getitem__(self, item):
        if isinstance(item, slice):
            raise Exception("Subclass disallows slicing")

        return self._container[item]

    def __len__(self) -> int:
        return len(self._container)

foo: MySequence[str] = MySequence()
reveal_type(foo[0])
Run Code Online (Sandbox Code Playgroud)

(请注意,我创建了typevar协变.严格来说,这不是必需的,但如果容器实际上是表示"只读"类型的结构,那么我们也可以获得最大的灵活性.)


注意:mypy在第一个示例中确定返回类型为Any的事实是预期的行为.根据PEP 484,任何没有类型注释的方法或签名都被视为参数和返回类型都是Any.

这是一种设计机制,默认情况下,无类型Python代码被视为完全动态.

Mypy内置了各种命令行参数,您可以尝试强制它来检查无类型函数的内容(我相信它是--check-untyped-defs?),尽管它不会尝试推断返回类型是什么.