在 Python 中使用适当的类型提示对 Sequence 进行子类化
Subclassing Sequence with proper type hints in Python
我正在尝试在 Python 中实现一种自定义序列 class:
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)
现在我想检查 mypy 是否知道 MySequence
的元素是 T
:
类型的项目
foo: MySequence[str] = MySequence()
reveal_type(foo[0])
# Revealed type is 'Any'
所以它失败了:mypy 对 foo
的项目一无所知。普通 Sequence
的相同示例有效:
bar: Sequence[str] = []
reveal_type(bar[0])
# Revealed type is 'builtins.str*'
如果我尝试向 __getitem__
实现添加类型注释,我会遇到另一个错误:
def __getitem__(self, idx) -> T:
# Signature of "__getitem__" incompatible with supertype "Sequence"
我也试过了
def __getitem__(self, idx) -> Union[T, Sequence[T]]:
因为 idx
可以是一个切片,在这种情况下,我的代码将 return 一个序列而不是一个元素。它失败并显示相同的消息。
正如在 my previous question, there is an open discussion 中讨论的那样。
但是,我仍然想知道,是否可以创建允许 mypy 提取有关其项目类型的信息的自定义序列类型,就像我的示例中那样?
在这种情况下,正确的做法是正确覆盖 exact signature for __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])
(请注意,我使 typevar 协变。严格来说,这不是必需的,但如果容器实际上是为了表示“只读”类型的结构,我们不妨最大灵活性。)
注意:mypy 在第一个示例中确定 return 类型为 Any 这一事实是预期的行为。根据 PEP 484,任何没有类型注释的方法或签名都被视为参数和 return 类型都是 Any
.
这是一种机制,旨在让未类型化的 Python 代码在默认情况下被视为完全动态的。
Mypy 内置了各种命令行参数,您可以使用它们来尝试强制它检查无类型函数的内容(我相信它是 --check-untyped-defs
?),尽管它不会尝试推断 return 类型是什么。
我正在尝试在 Python 中实现一种自定义序列 class:
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)
现在我想检查 mypy 是否知道 MySequence
的元素是 T
:
foo: MySequence[str] = MySequence()
reveal_type(foo[0])
# Revealed type is 'Any'
所以它失败了:mypy 对 foo
的项目一无所知。普通 Sequence
的相同示例有效:
bar: Sequence[str] = []
reveal_type(bar[0])
# Revealed type is 'builtins.str*'
如果我尝试向 __getitem__
实现添加类型注释,我会遇到另一个错误:
def __getitem__(self, idx) -> T:
# Signature of "__getitem__" incompatible with supertype "Sequence"
我也试过了
def __getitem__(self, idx) -> Union[T, Sequence[T]]:
因为 idx
可以是一个切片,在这种情况下,我的代码将 return 一个序列而不是一个元素。它失败并显示相同的消息。
正如在 my previous question, there is an open discussion 中讨论的那样。
但是,我仍然想知道,是否可以创建允许 mypy 提取有关其项目类型的信息的自定义序列类型,就像我的示例中那样?
在这种情况下,正确的做法是正确覆盖 exact signature for __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])
(请注意,我使 typevar 协变。严格来说,这不是必需的,但如果容器实际上是为了表示“只读”类型的结构,我们不妨最大灵活性。)
注意:mypy 在第一个示例中确定 return 类型为 Any 这一事实是预期的行为。根据 PEP 484,任何没有类型注释的方法或签名都被视为参数和 return 类型都是 Any
.
这是一种机制,旨在让未类型化的 Python 代码在默认情况下被视为完全动态的。
Mypy 内置了各种命令行参数,您可以使用它们来尝试强制它检查无类型函数的内容(我相信它是 --check-untyped-defs
?),尽管它不会尝试推断 return 类型是什么。