Почему bytearray не является Sequence в Python 2?

Я вижу странное несоответствие в поведении между 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 как раскрывающий протокол последовательности, который, глядя на исходный код кажется, что он должен, но любое понимание этого всего очень приветствуется.

Ответ 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):

Добавьте ABC ByteString, который объединяет байты и bytearray (но не memoryview). Там нет ABC для "объектов API буфера стиля PEP 3118", потому что нет способ распознать их в Python (кроме попытки использовать memoryview() на них).

И еще информация в PEP 3119:

Это предложение добавить поддержку базового класса (ABC) для Python 3000. Он предлагает: [...]  Конкретные ABC для контейнеров и итераторов, которые будут добавлены к    коллекции.

Значительная часть мышления, внесённого в предложение, касается не специфический механизм ABC, в отличие от интерфейсов или общих Функции (GF), но о разъяснении философских вопросов типа "что делает набор", "что делает отображение" и "что делает последовательность".

[...] метакласса для использования с ABC, что позволит нам добавить ABC как "виртуальный базовый класс" (не та же концепция, что и в С++) в любой класс, в том числе в другую ABC. Это позволяет стандартной библиотеке определять последовательность ABC и MutableSequence и регистрировать их как виртуальные базовые классы для встроенных типов, таких как basestring, tuple и list, так что, например, все условия истинны: [...] issubclass (bytearray, MutableSequence).

Только FYI memoryview был зарегистрирован как подкласс Sequence только в Python 3.4:

Там нет привязки для этого из-за смешения последовательности/отображения так что это простая пропущенная явная регистрация.

(подробнее см. issue18690).


PySequence_Check из API Python C не полагается на модуль 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 (пример для bytearray), и если это удастся, для ненулевого sq_item (в основном это getitem - пример для bytearray).

Ответ 2

Когда вы смотрите на исходный код абстрактных классов collections, вы увидите, что в подклассе python3 (файл _collections_abc.py) класса Sequence, class ByteString, register сам с bytearray, в то время как в python2 (файл _abcoll.py) нет класса ByteString, а Sequence не register сам с bytearray.

Под register Я имею в виду, что абстрактный класс Sequence (или его подкласс ByteString) вызывает метод abc.ABCMeta.register, который как сказано в описании этого подкласса Register подкаста как "виртуальный подкласс" этой ABC.

Я думаю, что это вызывает различное поведение между py2 и py3, но IMHO это ошибка (или лучше исправлена ​​ошибка в py3).