【问题标题】:Why is bytearray not a Sequence in Python 2?为什么 bytearray 在 Python 2 中不是序列?
【发布时间】:2015-11-22 08:40:10
【问题描述】:

我发现 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

但不是在 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

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

我实际上更担心 C API 函数 PySequence_Check 正确识别 PyByteArray_Type 类型的对象作为暴露序列协议,通过查看源代码似乎应该这样做,但任何洞察这整个事情很受欢迎。

【问题讨论】:

标签: python bytearray python-internals abc


【解决方案1】:

来自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

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

Sequence.register(tuple)
Sequence.register(basestring)
Sequence.register(buffer)
Sequence.register(xrange)
...
MutableSequence.register(list)

此行为在 Python 3.0 中已更改(特别是在 this commit 中):

添加统一bytesbytearray的ABC ByteString(但不是memoryview)。 “PEP 3118 样式缓冲区 API 对象”没有 ABC,因为没有 在 Python 中识别这些的方法(除了尝试使用 memoryview() 在他们身上)。

还有更多信息在PEP 3119:

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

提案中的大部分想法都不是关于 与接口或通用相比,ABC 的特定机制 函数(GFs),但关于澄清诸如“什么”之类的哲学问题 构成集合”、“什么构成映射”和“什么构成序列”。

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

仅供参考,memoryview 仅在 Python 3.4 中注册为 Sequence 的子类:

由于 Sequence/Mapping 混淆,因此没有鸭式输入 所以这是一个简单的缺少显式注册。

(详见issue18690)。


来自 Python C API 的PySequence_Check 不依赖于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;
}

它检查非零 tp_as_sequence 字段 (example for bytearray),如果成功,检查非零 sq_item 字段(基本上是 getitem - example for bytearray)。

【讨论】:

    【解决方案2】:

    当你查看collections抽象类的源代码时,你会看到在python3(文件_collections_abc.py)子类Sequence类,类ByteStringregister本身与bytearray在python2(文件_abcoll.py)没有ByteString类,Sequence本身没有registerbytearray

    register 我的意思是抽象类Sequence(或它的子类ByteString)正在调用abc.ABCMeta.register 方法,正如对该方法的描述中所说的那样将子类注册为的“虚拟子类”这个ABC。

    我认为这导致了 py2 和 py3 之间的不同行为,但恕我直言,这是错误(或者更好地修复了 py3 中的错误)。

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 2019-05-04
      • 2015-01-07
      • 1970-01-01
      • 2022-06-12
      • 1970-01-01
      • 2011-01-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多