__getitem__ 应先用 isinstance(key, slice) 判断再处理:调用 key.indices(len(self._data)) 获取规范的 (start, stop, step),再遍历取值;切片应返回同类型实例而非原生列表,以保持行为一致性。

__getitem__ 怎么处理 slice 对象?
直接判断 key 是否为 slice 类型,而不是假设它一定是整数。很多初学者写 __getitem__(self, key) 时只处理 int,一遇到 obj[1:5] 就抛 TypeError: slice indices must be integers or None or have an __index__ method。
- 用
isinstance(key, slice)分支处理切片逻辑 -
slice对象有start、stop、step三个属性,可能为None,别直接算索引 - 推荐用
key.indices(len(self._data))把它转成明确的 (start, stop, step) 元组,自动处理负索引和越界
例如:
def __getitem__(self, key):
if isinstance(key, slice):
start, stop, step = key.indices(len(self._data))
return [self._data[i] for i in range(start, stop, step)]
return self._data[key]
为什么不能只返回 list 切片结果?
返回原生 list 会丢失自定义类的语义和行为——比如你希望切片后仍是同一个类的实例,支持链式调用或额外方法。
- 若需保持类型一致,应构造新实例,如
return self.__class__(self._data[start:stop:step]) - 注意构造函数是否接受这种参数;若内部数据结构复杂(如带缓存、状态),需深拷贝或显式复制逻辑
- 不处理时,
obj[::2].some_custom_method()会报 AttributeError
__getitem__ 被调用的边界场景有哪些?
除了常规 obj[i] 和 obj[i:j:k],Python 还会在多个地方隐式触发它:
-
for item in obj:—— 触发obj[0]、obj[1]… 直到IndexError -
len(obj)不调用__getitem__,但某些第三方库(如 NumPy 兼容层)可能依赖它模拟长度 -
in操作符:如x in obj默认按顺序调用__getitem__,直到匹配或 IndexError - 解包:
a, b, *rest = obj同样依赖__getitem__
这意味着,如果切片返回类型不对、或没正确处理空切片(如 [10:5]),这些操作都可能意外失败。
性能与兼容性要注意什么?
频繁切片时,每次都新建列表或实例会有开销;而某些场景(如大数据容器)需要惰性求值。
- 避免在
__getitem__中做重计算——比如每次切片都重新解析文件或查数据库 - 如果底层是 NumPy 数组或 Pandas Series,优先复用其切片逻辑,而非手动遍历
- Python 3.9+ 的
typing.Sequence协议要求__getitem__支持int和slice,否则静态类型检查(如 mypy)会警告
切片逻辑本身不难,难的是让它的行为和内置类型对齐:负索引、省略边界、步长为负、空序列处理……这些细节漏一点,下游用起来就容易崩。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











