如何正确继承tuple类并实现索引切片,满足mypy检查?
解决继承
tuple[int, ...]时的__getitem__重载问题 首先明确:你确实误用了singledispatchmethod——这个装饰器不适合用来重载特殊方法(比如__getitem__),尤其是在需要通过mypy类型检查的场景下,它会导致类型签名冲突,也就是你遇到的“__getitem__重复定义”错误。
正确实现方案
我们需要用@overload来明确两种__getitem__的类型签名,同时手动处理索引和切片的返回逻辑,确保切片返回MyIntTuple实例而非原生tuple。
完整代码示例:
from typing import overload, Self, Tuple class MyIntTuple(tuple[int, ...]): @overload def __getitem__(self, key: int) -> int: ... @overload def __getitem__(self, key: slice) -> Self: ... def __getitem__(self, key: int | slice) -> int | Self: # 处理索引情况 if isinstance(key, int): return super().__getitem__(key) # 处理切片情况:先获取原生tuple切片结果,再包装成MyIntTuple sliced_tuple = super().__getitem__(key) return MyIntTuple(sliced_tuple) # 可选:定义__new__确保实例创建符合类型要求 @classmethod def __new__(cls, iterable: Tuple[int, ...]) -> Self: return super().__new__(cls, iterable)
关键说明
@overload的作用:- 给mypy明确两种调用场景的类型签名:传入
int索引返回int,传入slice返回MyIntTuple实例。 - 避免了重复定义的错误,因为mypy会识别这些重载是同一方法的不同类型分支。
- 给mypy明确两种调用场景的类型签名:传入
切片返回自定义实例:
- 调用父类的
__getitem__拿到切片后的原生tuple,再用MyIntTuple包装返回,这样就实现了切片返回自定义类型的需求。
- 调用父类的
为什么不用
singledispatchmethod:singledispatchmethod是基于函数参数的类型分发,但特殊方法的调用逻辑(比如obj[key])会绕过它的分发机制,导致实际运行时可能不符合预期。- mypy对
singledispatchmethod修饰的特殊方法类型推断支持不佳,容易触发重复定义的错误提示。
测试验证
运行以下代码,同时用mypy检查:
t = MyIntTuple((1, 2, 3, 4)) print(t[0]) # 输出1,类型为int print(t[1:3]) # 输出MyIntTuple((2, 3)),类型为MyIntTuple print(type(t[1:3])) # 输出<class '__main__.MyIntTuple'>
mypy不会再提示重复定义错误,且能正确推断返回值类型。
内容的提问来源于stack exchange,提问作者hiro protagonist
相关产品推荐
相关产品推荐

