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