什么是Python';s序列协议?

什么是Python';s序列协议?,python,sequence,cpython,python-internals,Python,Sequence,Cpython,Python Internals,Python使用了很多神奇的方法,其中大部分都是一些协议的一部分。我熟悉“迭代器协议”和“数字协议”,但最近在这个术语上遇到了问题。但即使经过一些研究,我也不确定“序列协议”是什么 例如,C API函数检查(根据文档)某个对象是否实现了“序列协议”。指示这是一个类,它不是dict,而是实现了一个\uuuuu getitem\uuuuuu方法,该方法与上的文档所述内容大致相同: […]必须支持序列协议(整数参数从0开始的\uuu getitem\uuu()方法)。[…] 但是从0开始的要求并不是在

Python使用了很多神奇的方法,其中大部分都是一些协议的一部分。我熟悉“迭代器协议”和“数字协议”,但最近在这个术语上遇到了问题。但即使经过一些研究,我也不确定“序列协议”是什么

例如,C API函数检查(根据文档)某个对象是否实现了“序列协议”。指示这是一个类,它不是dict,而是实现了一个
\uuuuu getitem\uuuuuu
方法,该方法与上的文档所述内容大致相同:

[…]必须支持序列协议(整数参数从0开始的
\uuu getitem\uuu()
方法)。[…]

但是从
0
开始的要求并不是在
PySequence\u Check
中“实现”的

然后还有一个类型,它基本上说实例必须实现
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

但是根据这个定义,实现“序列协议”的类不一定是序列,例如,抽象类garantuee表示序列有长度。但是,当使用
len(该类的一个实例)时,一个刚刚实现
\uu getitem\uuuuuuuuu
(通过
PySequence\u检查
)的类抛出一个异常


有人能帮我澄清一下序列和序列协议之间的区别吗(如果除了阅读源代码之外还有协议的定义),以及什么时候使用哪个定义?

它并不完全一致

这是:

PySequence\u Check
检查对象是否提供C序列协议,该协议通过表示对象类型的
PyTypeObject
中的
tp\u as\u序列
成员实现。此
tp_as_sequence
成员是指向一个结构的指针,该结构包含一系列用于序列行为的函数,例如
sq_item
用于通过数字索引检索项目,以及
sq_ass_item
用于项目分配

具体来说,
PySequence\u Check
要求它的参数不是dict,并且它提供
sq\u项

用Python编写的带有
\uuuu getitem\uuuuu
的类型将提供
sq\u item
,无论它们是概念序列还是映射,因此用Python编写的映射如果不是从
dict
继承的,将通过
PySequence\u检查



另一方面,
collections.abc.Sequence
仅检查对象是否具体继承自
collections.abc.Sequence
,或者其类(或超类)是否显式地
注册
collections.abc.Sequence
。如果您只是自己实现一个序列,而不执行这两项操作,它将不会通过
isinstance(您的序列,序列)
。另外,大多数注册到
collections.abc.Sequence
的类并不支持所有
collections.abc.Sequence
的方法。总的来说,
collections.abc.Sequence
的可靠性比人们通常预期的要低得多



至于在实践中什么是序列,它通常是任何支持
\uuu len\uuu
\uuu getitem\uuu
且整数索引从0开始且不是映射的东西。如果一个函数的文档说它需要任何序列,那几乎总是它需要的全部。不幸的是,“不是映射”很难测试,原因与“是序列”很难确定的原因类似。

要使类型符合序列协议,必须满足以下4个条件:

  • 按索引检索元素

    item=seq[索引]

  • 按值查找项目

    index=序号索引(项目)

  • 清点物品

    num=序号计数(项目)

  • 产生相反的序列

    r=反向(seq)


collections.abc.Sequence
需要
\uuuu getitem\uuuuuuuuuuuuuuuu
\uuuuuu len\uuuuuuuuuuuuuuuuu
。其他一切都有mixin方法。关于迭代,如果只定义了
\uuuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。要使
反向
工作,还必须定义
\uuuu len\uuuu
,以便它可以从最后一个索引开始。@eryksun但类不需要
\uuuuu len\uuuu
来实现序列协议(就
PySequence\u Check
而言)。一个类实现了
\uu len\uuu
\uu getitem\uuu
,但没有从
集合继承。abc.Sequence
没有传递
isinstance(实例,序列)
。这就是引发我的问题的原因。:)这里有一个关于
PySequence\u Check
的详细解释:我认为
PySequence\u Check
排除了
dict
s,因为子类可以实现
\uuuuu getitem\uuuuu
,而对于自定义类,它们在实现
\uu getitem\uuuu
时返回
True
。谢谢你的回答(特别是指出
collections.abc.Sequence
没有
\uuuu子类hook\uuuuuuu
对我来说是新的),我会把它保留一天,以防其他人想提供答案。@mseifer:是的,我对用户定义的类和
sq\u项的理解是错误的。我可以发誓,通过包装
\uuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。它似乎总是不必要地局限于我。@eryksun我已经问Guido他是否可以在你的问题中添加缺少的
\uuuuuu子类hook\uuuuuu
,因为它仍然是开放的。顺便问一下,你知道为什么这张桌子这么少吗
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;
}