在 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 数据结构的基石之一,也是理解容器与迭代行为不可或缺的一环。
![]()
“点赞有美意,赞赏是鼓励”
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.