什么是Python的序列协议?

时间:2017-04-23 00:16:00

标签: python sequence cpython python-internals

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)时会抛出异常。

有人可以告诉我序列和序列协议之间的区别(如果除了阅读源代码之外还有协议的定义)以及何时使用哪个定义?

2 个答案:

答案 0 :(得分:13)

这不是真的一致。

这是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要求其参数不是dict,并且它提供sq_item

使用Python编写的__getitem__类型将提供sq_item,无论它们是概念序列还是映射,因此用Python编写的不从dict继承的映射将会通过PySequence_Check

另一方面,collections.abc.Sequence仅检查对象是否具体继承自collections.abc.Sequence,或者其类(或超类)是否registercollections.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)