在 Python 中,'Sequence' 协议定义了可索引和可迭代对象的接口,例如列表或元组。历史上,序列几乎在 Python 的早期版本中就出现了,以支持对数据结构的自然操作:索引、切片、元素遍历。
问题 - 为了让用户定义的类表现得像一个序列,仅仅实现 iter 和 next 方法是不够的。为了完全支持序列行为,需要额外的方法。
解决方案 - 要实现自定义的序列类型,需要定义 getitem 方法(这是索引和切片所必需的),并可选地定义 len 方法(用于 len() 和检查长度)。这样,对象将支持迭代、索引访问、切片操作,以及许多 Python 对序列的标准操作。
代码示例:
class MyCounter: def __init__(self, stop): self._stop = stop def __getitem__(self, index): if 0 <= index < self._stop: return index * 10 else: raise IndexError('超出范围') def __len__(self): return self._stop c = MyCounter(5) print(c[3]) # 30 print(len(c)) # 5 for x in c: print(x)
关键特性:
如果我仅实现 iter 和 next,我的对象会成为一个序列(Sequence)吗?
不。这样的对象只是一个可迭代对象,但不是一个序列。它不支持索引、切片和标准的类似列表的对象功能。
一定要实现 getitem 才能支持 for 循环吗?
不一定。如果实现了 iter,for 循环将会工作。但如果没有 iter,解释器会尝试使用 getitem,从索引 0 开始,直到出现 IndexError。因此,序列只需 getitem 就足够了。
可以仅为 int 实现 getitem,而不为 slice 吗?
从技术上讲,对 c[0] 的访问是可以的,但尝试获取切片 c[1:4] 将会失败。为了支持切片,getitem 必须能够处理 slice 类型的对象(请参阅 slice.indices 和 isinstance(key, slice))。
代码示例:
class S: def __getitem__(self, idx): if isinstance(idx, slice): return [x for x in range(idx.start or 0, idx.stop or 10, idx.step or 1)] return idx * 2
实现了自定义结构,仅定义了 iter,以为现在可以使用切片和索引。
优点:
缺点:
类实现了 getitem,支持切片和整数索引,以及 len。
优点:
缺点: