Python使用魔术方法做了很多工作,其中大部分是一些协议的一部分。我熟悉“迭代器协议”和“数字协议”,但最近偶然发现了术语"sequence protocol"。但即使经过一些研究,我也不确定“序列协议”是什么。
例如,C API函数PySequence_Check
检查(根据文档)某个对象是否实现了“序列协议”。 source code表示这是一个不是dict的类,但实现的__getitem__
方法大致与iter
上的文档相同<:> / p>
[...]必须支持序列协议(
__getitem__()
方法,整数参数从0开始)。[...]
但以0
开头的要求不是PySequence_Check
中“已实施”的要求。
然后还有collections.abc.Sequence
类型,基本上说实例必须实现__reversed__
,__contains__
,__iter__
和__len__
。
但是根据该定义,实现“序列协议”的类不一定是序列,例如"data model"和序列具有长度的抽象类garantuee。但是,只使用__getitem__
(传递PySequence_Check
)的类在使用len(an_instance_of_that_class)
时会抛出异常。
有人可以告诉我序列和序列协议之间的区别(如果除了阅读源代码之外还有协议的定义)以及何时使用哪个定义?
答案 0 :(得分:13)
这不是真的一致。
int
PySequence_Check(PyObject *s)
{
if (PyDict_Check(s))
return 0;
return s != NULL && s->ob_type->tp_as_sequence &&
s->ob_type->tp_as_sequence->sq_item != NULL;
}
PySequence_Check
检查对象是否提供C序列协议,通过代表对象类型的tp_as_sequence
中的PyTypeObject
成员实现。这个tp_as_sequence
成员是一个指向结构的指针,该结构包含一系列用于序列行为的函数,例如sq_item
用于通过数字索引检索项目,sq_ass_item
用于项目赋值。
具体来说,PySequence_Check
要求其参数不是dict,并且它提供sq_item
。
使用Python编写的__getitem__
类型将提供sq_item
,无论它们是概念序列还是映射,因此用Python编写的不从dict
继承的映射将会通过PySequence_Check
。
另一方面,collections.abc.Sequence
仅检查对象是否具体继承自collections.abc.Sequence
,或者其类(或超类)是否register
与collections.abc.Sequence
一起显式。如果您只是自己实现序列而不执行其中任何一项,则不会传递isinstance(your_sequence, Sequence)
。此外,注册collections.abc.Sequence
的大多数类都不支持collections.abc.Sequence
的所有方法。总体而言,collections.abc.Sequence
的可靠性远低于人们通常预期的可靠性。
至于什么算作实际中的序列,它通常是支持__len__
和__getitem__
的任何东西,其整数索引从0开始并且不是映射。如果函数的文档说它需要任何序列,那几乎总是它需要它。不幸的是,“不是映射”很难测试,原因类似于“序列”很难确定。
答案 1 :(得分:1)
要使类型符合序列协议,必须满足以下4个条件:
按索引检索元素
item = seq[index]
按值查找项目
index = seq.index(item)
计数项目
num = seq.count(item)
产生反向序列
r = reversed(seq)