在 Python 的对象体系中,序列协议(Sequence Protocol)定义了对象以固定顺序访问和操作其元素的约定行为。

它是列表(list)、元组(tuple)、字符串(str)、range 等类型的共同基础,也常被自定义容器实现,以支持切片、索引、迭代等核心操作。

序列是容器协议的特化形式,但比一般容器更具“线性结构”和“顺序访问”特征。

理解序列协议,能帮助我们编写出既自然又与内建类型高度兼容的自定义对象。

一、什么是序列协议

在 Python 官方语言参考手册中,序列类型(sequence types)被描述为:

“Objects which implement the sequence protocol, that is, they support the __len__() and __getitem__() methods of containers with integer keys.”

也就是说,一个对象若想被视为序列(sequence),只需实现:

__len__(self) → 返回序列长度。

__getitem__(self, index) → 根据整数索引或切片访问元素。

如果希望支持元素修改或删除,还可实现:

__setitem__(self, index, value) → 元素赋值。

__delitem__(self, index) → 删除元素。

此即为 “序列协议” 的核心。

虽然官方文档未以“Sequence Protocol”为正式标题列出,但该行为规范已被广泛接受并长期存在于 Python 语言参考中。

二、基本行为与 for 循环的衔接

当你在 for 循环中使用一个序列时:

    print(x)

Python 的内部逻辑如下:

1、尝试调用 iter(seq);

2、若对象未定义 __iter__(),则回退使用 __getitem__();

3、从索引 0 开始,逐步访问元素,直到抛出 IndexError 为止。

也就是说:

即使一个类没有实现可迭代协议(__iter__() / __next__()),只要具备 __getitem__(),也能自然参与 for 循环。

这体现了 Python 协议体系的“鸭子类型哲学”:不必继承接口,只要行为符合约定即可被视为兼容。

三、索引与切片机制

(1)整数索引访问

当你执行:

value = seq[i]

Python 实际调用

seq.__getitem__(i)

• 若 i 超出范围,则触发 IndexError。

• 若 i 为负值,解释器自动换算为 len(seq) + i。

(2)切片访问

当你执行:

part = seq[1:4]

解释器传入的参数并非整数,而是一个 对象:

seq.__getitem__(slice(1, 4, None))

因此,自定义类若想支持切片,只需判断参数类型:

print(seq[1:3].data)  # 输出 [20, 30]

• 创建 MySeq 实例,包含 [10, 20, 30, 40]。

• seq[1:3] 触发切片操作,返回包含 [20, 30] 的新 MySeq 实例。

• .data 访问新实例的 data 属性,输出 [20, 30]。

四、可变与不可变序列

根据是否允许修改,序列可分为两大类:

(1)不可变序列(Immutable Sequences)

• 典型代表:tuple、str、range

• 实现:仅支持 __getitem__()、__len__()

• 不支持元素修改或删除

(2)可变序列(Mutable Sequences)

• 典型代表:list、bytearray

• 实现:除 __getitem__()、__len__() 外,还支持 __setitem__()、__delitem__()、append()、extend() 等方法

在协议意义上,不可变序列仅需支持“读取行为”;而可变序列则需在此基础上扩展“修改行为”,从而构成完整的“可变容器”协议子集。

五、示例:自定义序列类

下面是一个完整示例,展示如何实现一个简化的“动态序列”:

    print(x)

输出结果:

40

该对象完全符合 Python 的序列行为,可与 for 循环、切片操作、len() 等函数无缝协作。

六、与容器协议的关系

序列协议可以视为容器协议的“定制版”:

协议

核心行为

是否有顺序

容器协议

Container Protocol

__contains__

__getitem__

__len__

不保证

序列协议

Sequence Protocol

__getitem__

__len__ (整数键)

保证顺序

容器协议关注“元素是否存在”,而序列协议进一步强调“元素的顺序与位置”。

• 所有序列都是容器。

• 但并非所有容器都是序列(例如集合 set 和字典 dict 就不保证顺序)。

小结

序列协议让 Python 的对象世界具备了“线性访问”的共性。

它的核心是两个方法:

__len__():告诉解释器对象的长度.

__getitem__():定义如何按索引或切片取值。

凭借这两点,一个对象即可:

被 for 循环遍历。

支持索引访问与切片。

与内建函数如 len()、list()、tuple() 等完美兼容。

正因如此,序列协议成为 Python 数据结构的基石之一,也是理解容器与迭代行为不可或缺的一环。

打开网易新闻 查看精彩图片

点赞有美意,赞赏是鼓励