为什么bytearray不是Python 2中的Sequence?

Jai*_*ime 12 python bytearray abc python-internals

我看到Python 2和3之间的行为有一种奇怪的差异.

在Python 3中,似乎工作正常:

Python 3.5.0rc2 (v3.5.0rc2:cc15d736d860, Aug 25 2015, 04:45:41) [MSC v.1900 32 b
it (Intel)] on win32
>>> from collections import Sequence
>>> isinstance(bytearray(b"56"), Sequence)
True
Run Code Online (Sandbox Code Playgroud)

但不是在Python 2中:

Python 2.7.10 (default, May 23 2015, 09:44:00) [MSC v.1500 64 bit (AMD64)] on wi
n32
>>> from collections import Sequence
>>> isinstance(bytearray("56"), Sequence)
False
Run Code Online (Sandbox Code Playgroud)

在Python 2.x和3.x的次要版本中,结果似乎是一致的.这是一个已知的错误?它是一个bug吗?这种差异背后有什么逻辑吗?

我实际上更担心C API函数PySequence_Check正确识别类型的对象,PyByteArray_Type因为它暴露了序列协议,通过查看它看起来应该的源代码,但是对这整个事物的任何洞察都是非常受欢迎的.

vau*_*tah 7

抽象类从collections使用ABCMeta.register(subclass)

子类注册为此ABC的"虚拟子类".

在Python 3中issubclass(bytearray, Sequence)返回True因为bytearray显式注册为子类ByteString(派生自Sequence)和MutableSequence.请参阅Lib/_collections_abc.py的相关部分:

class ByteString(Sequence):

    """This unifies bytes and bytearray.

    XXX Should add all their methods.
    """

    __slots__ = ()

ByteString.register(bytes)
ByteString.register(bytearray)
...
MutableSequence.register(bytearray)  # Multiply inheriting, see ByteString
Run Code Online (Sandbox Code Playgroud)

Python 2没有这样做(来自Lib/_abcoll.py):

Sequence.register(tuple)
Sequence.register(basestring)
Sequence.register(buffer)
Sequence.register(xrange)
...
MutableSequence.register(list)
Run Code Online (Sandbox Code Playgroud)

在Python 3.0中更改了此行为(在此提交中):

添加ABC ByteString,它统一了字节bytearray(但不是内存视图)."PEP 3118样式缓冲API对象"没有ABC,因为在Python中无法识别这些(除了尝试 在它们上使用memoryview()).

PEP 3119中有更多信息:

这是向Python 3000添加抽象基类(ABC)支持的提议.它建议:[...]容器和迭代器的特定ABC,将添加到集合模块.

进入提案的大部分思路都不是关于ABCs的具体机制,与接口或通用功能(GF)相比,而是关于澄清哲学问题,如"什么使一组","什么使映射"和"什么使序列".

[...]一个与ABC一起使用的元类,它允许我们将ABC作为"虚拟基类"(与C++中的概念不同)添加到任何类,包括另一个类.这允许标准库定义ABCs SequenceMutableSequence并将它们注册为内置类型(如basestring,tuplelist)的虚拟基类,以便例如以下条件都为真:[...] issubclass(bytearray, MutableSequence).

只是FYI memoryview被注册为Sequence仅在Python 3.4中的子类:

由于序列/映射混淆,因此没有出现这种情况,因此这是一个简单的缺少显式注册.

(详情请参见issue18690).


PySequence_Check来自Python C API不依赖于collections模块:

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;
}
Run Code Online (Sandbox Code Playgroud)

它检查非零tp_as_sequence字段(示例为bytearray),如果成功,则检查非零sq_item字段(基本上是getitem - 示例bytearray).