在 Python 中使用适当的类型提示对序列进行子类化 [英] Subclassing Sequence with proper type hints in Python
问题描述
我正在尝试在 Python 中实现一种自定义序列类:
I'm trying to implement a kind of custom sequence class in 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)
现在我想检查 mypy 是否知道 MySequence
的元素是 T
类型的项目:
Now I want to check that mypy is aware that elements of MySequence
are items of type T
:
foo: MySequence[str] = MySequence()
reveal_type(foo[0])
# Revealed type is 'Any'
所以它失败了:mypy 对 foo
的项目一无所知.普通 Sequence
的相同示例有效:
So it fails: mypy knows nothing about items of foo
. The same example for ordinary Sequence
works:
bar: Sequence[str] = []
reveal_type(bar[0])
# Revealed type is 'builtins.str*'
如果我尝试将类型注释添加到 __getitem__
实现,则会出现另一个错误:
If I'm trying to add type annotations to __getitem__
implementation, I have another error:
def __getitem__(self, idx) -> T:
# Signature of "__getitem__" incompatible with supertype "Sequence"
我也试过
def __getitem__(self, idx) -> Union[T, Sequence[T]]:
as idx
可以是一个切片,在这种情况下,我的代码将返回一个序列而不是一个元素.它失败并显示相同的消息.
as idx
can be a slice and in that case my code will return a sequence instead of one element. It fails either with the same message.
As discussed in my previous question, there is an open discussion on issues like that.
但是,我仍然想知道,是否可以创建自定义序列类型以允许 mypy 提取有关其项目类型的信息,就像在我的示例中一样?
However, I still wonder, is it possible to create custom sequence types that allow mypy to extract information about the type of its items, like in my example?
推荐答案
在这种情况下,正确的做法是正确覆盖 __getitem__
的精确签名,包括重载.
In this case, the correct thing to do is to correctly override the exact signature for __getitem__
, including the overloads.
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 协变.严格来说,这不是必需的,但如果容器有效地表示只读"类型的结构,我们不妨以获得最大的灵活性.)
(Note that I made the typevar covariant. This isn't, strictly-speaking, required, but if the container is effectively meant to represent a "read-only" kind of structure, we might as well for maximum flexibility.)
注意:mypy 在第一个示例中决定返回类型为 Any 的事实是预期行为.根据 PEP 484,任何没有类型注释的方法或签名都被视为参数和返回类型都是 Any
.
Note: the fact that mypy decides that the return type is Any in the first example is expected behavior. According to PEP 484, any method or signature without type annotations is treated as if the argument and return types are all Any
.
这是一种机制,默认情况下,无类型的 Python 代码被视为完全动态的.
This is a mechanism designed so that untyped Python code is treated as fully dynamic by default.
Mypy 内置了各种命令行参数,您可以使用它们来尝试强制它检查无类型函数的内容(我相信它是 --check-untyped-defs
?),尽管它不会尝试推断返回类型是什么.
Mypy comes built-in with a variety of command line arguments you can use to try and coerce it to check the contents of untyped functions anyways (I believe it's --check-untyped-defs
?), though it won't attempt to infer what the return type is.
这篇关于在 Python 中使用适当的类型提示对序列进行子类化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!