12

我看到 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为公开序列协议,通过查看源代码似乎应该这样做,但是对整个事情的任何见解都是非常受欢迎的。

4

2 回答 2

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

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

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

这种行为在 Python 3.0 中发生了变化(特别是在这个提交中):

添加统一字节和字节数组(但不是memoryview)的ABC ByteString 。“PEP 3118 样式缓冲区 API 对象”没有 ABC,因为在 Python 中无法识别它们(除了尝试在它们上使用memoryview()之外 )。

PEP 3119中有更多信息:

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

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

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

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

由于序列/映射混淆,因此没有鸭式输入,因此它是一个简单的缺少显式注册。

(有关详细信息,请参阅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;
}

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

于 2015-08-27T20:02:38.207 回答
4

当您查看collections抽象类的源代码时,您会看到在 python3 (file _collections_abc.py) 中 class 的子Sequence类 class本身带有,ByteString而在 python2 (file ) 中没有class 并且本身不带有.registerbytearray_abcoll.pyByteStringSequenceregisterbytearray

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

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

于 2015-08-27T20:22:41.650 回答