我正在尝试subclass tuple
并在此过程中使mypy
快乐。我想使切片和索引工作。
以下是我尝试过的:
from functools import singledispatchmethod
from typing import Iterable
class MyIntTuple(tuple[int, ...]):
def __new__(cls, iterable: Iterable[int]) -> "MyIntTuple":
return super().__new__(cls, iterable) # type: ignore
@singledispatchmethod
def __getitem__(self, value: slice, /) -> "MyIntTuple":
return MyIntTuple(super().__getitem__(value))
@__getitem__.register
def __getitem__(self, value: int, /) -> int:
return super().__getitem__(value)
这几乎可以工作,除了切片:
a = MyIntTuple(range(12))
print(a) # (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
print(a[3]) # 3
print(a[3:6]) # (3, 4, 5)
b = a[5:8]
print(type(a)) # <class '__main__.MyIntTuple'>
print(type(b)) # <class 'tuple'>
这里的问题是切片应该返回MyIntTuple
的示例。好像搞错了。mypy
对此也不满意:
error: Name "__getitem__" already defined on line 488 [no-redef]
tuple
中子元素为
Superclass:
@overload
def __getitem__(self, SupportsIndex, /) -> int
@overload
def __getitem__(self, slice, /) -> Tuple[int, ...]
我如何正确地超载那些?functools.singledispatchmethod
是不是走错路了?还是我用错了?
3条答案
按热度按时间ojsjcaue1#
你需要给予每个实现一个不同于“main”函数的名字:
此外,切片的实现应该与“main”函数(它被用作无法识别的参数类型的后备)分开,int实现应该真正接受任何支持
__index__
的东西。由于
MyIntTuple
尚未定义,因此必须为切片实现显式地向register
传递类型,因此它不必查看注解。此外,您可以删除您的__new__
实现-继承的实现将正常工作:v09wglhw2#
overload
在签名mypy
中呈现给你的并不是一些抽象的东西,而是对来自typing
的decorator的引用。下面是匹配超类型定义的方法:
上面的代码片段显示了使用
int
的一个问题:这个限制应该被削弱,因为更窄的参数类型违反了LSP。实际的tuple
可以使用slice
或任何定义了__index__
dunder方法的东西,而不仅仅是__getitem__
中的int
。有了
overload
和SupportsIndex
,您几乎已经准备好了。您需要的唯一更改是交换重载顺序:mypy
强制执行相同的重载顺序,Sequence
将SupportsIndex
签名放在第一位(尝试将它们交换回来并观察错误)。在这种情况下,这并不重要,但通常不同的重载顺序可能会更改类型解析。重载按定义顺序尝试,最上面的重载获胜。如果签名重叠,通常您会看到一个额外的mypy
错误-但无论如何最好保持重载的顺序。下面是逆序会产生的结果:上面的代码typechecks与
--strict
。mwg9r5ms3#
singledispatchmethod
有问题。