2017-04-23 53 views
7

Python有很多神奇的方法,其中大部分都是某些协议的一部分。我熟悉“迭代器协议”和“数字协议”,但最近偶然发现了术语"sequence protocol"。但即使经过一些研究,我也不确定“序列协议”是什么。什么是Python的序列协议?

例如,C API函数PySequence_Check检查(根据文档)某些对象是否实现“序列协议”。该source code表明这是一类,这不是一个字典,但实现__getitem__方法是大致相同什么上iter文件还指出:

[...]必须支持序列协议(该__getitem__()方法用从0开始的整数参数)。[...]

但下手0要求不是那种在PySequence_Check“落实”。

然后里面还有collections.abc.Sequence类型,它基本上是说实例必须实现__reversed____contains____iter____len__

但是通过该定义,实施“序列协议”的类不一定是序列,例如"data model"和抽象类garantuee,序列具有长度。但是使用len(an_instance_of_that_class)时,仅执行__getitem__(传递PySequence_Check)的类会引发异常。

有人能请我澄清序列和序列协议(如果除了阅读源代码之外还有协议的定义)和何时使用哪种定义之间的区别?

+0

'collections.abc.Sequence'需要'__getitem__'和'__len__'。其他的一切都有mixin方法。关于迭代,如果只是'__getitem__'被定义为没有'__iter__',那么内建的'iter'实例化一个简单的迭代器,它从索引0开始。对于'revers'来工作'__len__'也必须被定义,所以它可以从最后一个索引开始。 – eryksun

+0

@eryksun但是一个类不需要'__len__'来实现序列协议(就“PySequence_Check”而言)。并且实现'__len__'和'__getitem__'但不从'collections.abc.Sequence'继承的类没有通过'isinstance(an_instance,Sequence)'。这就是触发我的问题的原因。 :) – MSeifert

回答

5

这不是真的一致。

这里的PySequence_Check

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要求其参数不是字典,并且它提供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开始的整数索引,而不是一个映射。如果一个函数的文档说它需要任何序列,那几乎总是它所需要的。不幸的是,“不是映射”很难测试,因为类似于“是一个序列”的原因难以确定。

+0

我认为'PySequence_Check'排除了'dict's,因为子类可以实现'__getitem__',因为它们在实现'__getitem__'时会返回'True':https://gist.github.com/MSeifert04/ e39d91f9d262618a32f7db14aaab15f4。谢谢你的回答(特别是指出'collections.abc.Sequence'没有'__subclasshook__'对我来说是新的),如果有人想提供答案,我会在一天内不接受它。 – MSeifert

+1

@ MSeifert:是的,我错了用户定义的类和'sq_item'。我可以发誓没有处理通过包装'__getitem__'来提供'sq_item',但显然存在,这不是新的。 – user2357112

+1

请参阅[issue 23864](http://bugs.python.org/issue23864)有关'issubclass'与ABC不是“一次性小马”的限制。这似乎总是不必要地限制在我身上。 – eryksun