__getitem__ 必须同时处理 int 和 slice:对 int 调用 _normalize_index 转负索引,对 slice 用 _normalize_slice 标准化边界并返回新序列实例。
自定义序列类要支持负索引和切片,核心是正确实现 __getitem__。它不能只假设传入的是 int——当写 obj[1:4] 或 obj[-2:] 时,Python 会传入一个 slice 对象,不是整数。如果没做类型分发,直接对 slice 做算术运算会抛 TypeError: unsupported operand type(s) for +: 'slice' and 'int'。
实操建议:
__getitem__ 开头用 isinstance(key, slice) 分支处理切片逻辑int 类型的 key,先用 self._normalize_index(key) 统一转为非负索引(避免重复写负数逻辑)__getitem__ 里手动调用 range() 或拼列表——切片可能很大,应返回新序列实例或惰性对象(如自定义迭代器)负索引(如 -1)的意义依赖于序列当前长度,不能硬编码偏移量。比如 myseq[-1] 应等价于 myseq[len(myseq)-1],而 len(myseq) 可能随内部数据变化。
实操建议:
__len__(self) 并确保它返回真实、及时的长度_normalize_index(self, i),内部用 i % len(self) 或 i + len(self) if i 转换;注意前者对 i = -0 和边界值更鲁棒
__getitem__ 中对整数索引统一调用该方法,切片的 .start/.stop/.step 也要分别归一化(slice.indices(len) 是标准解法)用户期望 mylist[1:3] 返回 list,mytuple[::2] 返回 tuple。同理,你的自定义类 MySeq 的切片也应返回 MySeq 实例,而不是 list 或 tuple。否则链式操作(如 obj[1:5][::-1])会意外降级为内置类型,丢失自定义行为。
实操建议:
type(self)(new_data) 构造新实例,而非 list(...)
start >= stop(正向)或 start (负步长)时,应返回空实例,而非报错
常见坑:内部用 list 存数据,但 __len__ 返回了错误值(比如缓存未更新)。这时 slice.indices(len) 计算出的索引范围会

实操建议:
append、pop)必须同步更新长度相关状态,或干脆每次 __len__ 都实时计算(只要代价可控)obj[-1]、obj[:-1]、obj[100:](越界)、obj[::2]、obj[::-1]
collections.abc.Sequence 的默认 __contains__ 或 index——它们底层会调用 __getitem__,如果切片/索引逻辑有缺陷,这些方法也会出错负索引和切片看似简单,真正难的是让所有边界情况下的行为和内置类型完全对齐——尤其是空序列、全负切片、超大步长这些场景,很容易被忽略。