Python 键入列表的子类

Python typing for a subclass of list

我希望能够定义列表的子class 的内容必须是什么。 class 如下所示。

class A(list):
   def __init__(self):
      list.__init__(self)

我想包括输入,以便发生以下情况。

import typing

class A(list: typing.List[str]):  # Maybe something like this
   def __init__(self):
      list.__init__(self)

>> a = A()
>> a.append("a")  # No typing error
>> a.append(1)  # Typing error

typing 方便地提供了 collections.MutableSequence 的通用版本,因此效果如下:

import typing

T = typing.TypeVar('T')
class HomogeneousList(typing.MutableSequence[T]):
    def __init__(self, iterable: typing.Iterable[T]=()) -> None:
        self._data: typing.List[T]  = []
        self._data.extend(iterable)

    @typing.overload
    def __getitem__(self, index: int) -> T: ...
    @typing.overload
    def __getitem__(self, index: slice) -> HomogeneousList[T]: ...
    def __getitem__(self, index):
        return self._data[index]

    @typing.overload
    def __setitem__(self, index: int,  item: T) -> None: ...
    @typing.overload
    def __setitem__(self, index: slice, item: typing.Iterable[T]) -> None: ...
    def __setitem__(self, index, item):
        self._data[index] = item

    def __delitem__(self, index: typing.Union[int, slice]) -> None:
        del self._data[index]

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

    def insert(self, index: int, item: T) -> None:
        self._data.insert(index, item)


string_list = HomogeneousList[str]()
string_list.append('foo')
string_list.append(42)


int_list = HomogeneousList[int]()
int_list.append(42)
int_list.append('foo')

现在,mypy出现以下错误:

test.py:36: error: Argument 1 to "append" of "MutableSequence" has incompatible type "int"; expected "str"
test.py:41: error: Argument 1 to "append" of "MutableSequence" has incompatible type "str"; expected "int"

输入 __getitem__ 等有一些棘手的方面,因为它们也接受 slice 对象,但并不可怕。

请注意,这很有用,因为如果您只是尝试这样做:

class HomogeneousList(collections.abc.MutableSequence, typing.Generic[T]):
    ....

MyPy,至少,不会抛出追加错误。 AFAIKT 你必须明确添加:'

def append(self, item: T) -> None:
    self._data.append(item)

从一开始就删除了 collections.abc.MutableSequence 的很多实用程序。无论如何,值得庆幸的是,键入提供了所有这些开箱即用的通用版本!

请注意,您可以像我展示的那样通用地使用这些,但您也可以执行以下操作:

class StringList(HomogeneousList[str]):
    pass

mylist = StringList([1,2,3]) # mypy error
mylist = StringList('abc') # no error

mylist.append('foo') # no error
mylist.append(42) # mypy error

在Python3.9之前,您可以使用:

import typing

class A(typing.List[str]):
    pass

这向您的类型检查器表明 class A 的元素应该是 str 类型。在 运行 时,这与创建 list 的子 class 相同。 PEP 484 specifies how the typing system behaves. In particular, the example in this section of the PEP 做的事情与您的要求类似,但使用 typing.Dict 而不是 typing.List

在 Python 3.9+ 中,您可以 use the built-in type instead of importing from typing。 class 变为:

class A(list[str]):
    pass