__getitem__ 方法使实例支持方括号访问,传入索引或键即触发;需按类型(int/slice/其他)分支处理,正确抛出 indexerror/keyerror,并与 __len__ 和迭代协议保持一致。

__getitem__ 的基本写法和触发条件
只要类定义了 __getitem__ 方法,实例就能用方括号语法(如 obj[0]、obj['key'])访问——Python 会自动把索引或键传给这个方法。它不依赖继承、也不需要实现其他协议,纯靠方法名触发。
常见错误是误以为必须返回列表或支持切片;其实只要方法能处理传入的 key 参数并返回合理值即可。比如字符串索引传的是 int,字典式访问传的是 str 或任意 hashable 类型。
- 参数名不限于
key,但语义上建议用key或index - 若传入非法索引(如越界整数),应主动抛出
IndexError或KeyError,否则可能静默失败 - 支持切片的关键是判断
key是否为slice类型:用isinstance(key, slice)分支处理
处理 int 索引与 slice 索引的差异
当用户写 obj[2] 或 obj[1:4] 时,key 分别是 int 和 slice 对象。不区分会导致切片报错或返回意外结果。
典型写法是先检查类型再分支处理:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
def __getitem__(self, key):
if isinstance(key, int):
if key = len(self._data):
raise IndexError("index out of range")
return self._data[key]
elif isinstance(key, slice):
return self._data[key]
else:
raise TypeError(f"unsupported key type: {type(key).__name__}")
-
slice对象自带.start、.stop、.step属性,可手动解析,但直接用于列表切片更安全 - 如果内部数据不支持切片(比如是生成器),必须自己实现切片逻辑,否则会抛
TypeError - 负索引默认由 Python 转换后传入,无需额外处理,除非你要禁用
为什么 dict 风格访问也走 __getitem__?
用 obj['name'] 访问时,__getitem__ 同样被调用,且 key 是字符串。这说明它不是“数组专属”,而是通用的键访问协议。
这意味着你可以用同一个方法同时支持位置索引和命名键,但要注意语义冲突:
- 如果类既想模拟序列又想模拟映射,需明确设计 key 类型规则(比如只接受
int或str) - 若内部用字典存储,直接返回
self._mapping[key]即可;若用列表,则需拒绝非 int 键,避免混淆 - 不建议在同一个
__getitem__中混用两种语义,容易让使用者困惑——比如obj[0]返回元素,obj['0']却报错
容易忽略的边界问题
最常被跳过的不是语法,而是协议一致性。比如实现了 __getitem__ 却没考虑 for item in obj: 的行为——Python 的 for 循环底层就是反复调用 __getitem__,从 0 开始直到抛出 IndexError 才停止。
- 如果
__getitem__对0、1、2… 都返回有效值,但长度实际有限,必须确保越界时抛IndexError,否则循环永不终止 - 若支持负索引,
obj[-1]应等价于obj[len(obj)-1],否则和内置类型行为不一致 - 如果类还实现了
__len__,请确保__getitem__的合法索引范围和len()返回值对齐,否则list(obj)可能出错
真正麻烦的从来不是写几行 __getitem__,而是让它和周边协议(迭代、长度、切片、异常)咬合得严丝合缝。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










