Python bytearray 是否在 C 表示中使用有符号整数?

Does Python bytearray use signed integers in the C representation?

我在Python中写了a small Cython tool for in-place sorting of structures exposing the buffer protocol。这是一项正在进行的工作;请原谅任何错误。仅供学习。

在我的一组单元测试中,我正在测试跨多种不同类型的缓冲区公开数据结构的就地排序,每种数据结构中都包含多种类型的基础数据。我可以验证它在大多数情况下都按预期工作,但 bytearray 的情况非常特殊。

如果您认为我在下面代码中导入的模块 b 只是在 bytearray 上就地在 Cython 中执行简单的堆排序,那么下面的代码示例显示问题:

In [42]: a #NumPy array
Out[42]: array([  9, 148, 115, 208, 243, 197], dtype=uint8)

In [43]: byt = bytearray(a)

In [44]: byt
Out[44]: bytearray(b'\t\x94s\xd0\xf3\xc5')

In [45]: list(byt)
Out[45]: [9, 148, 115, 208, 243, 197]

In [46]: byt1 = copy.deepcopy(byt)

In [47]: b.heap_sort(byt1)

In [48]: list(byt1)
Out[48]: [148, 197, 208, 243, 9, 115]

In [49]: list(bytearray(sorted(byt)))
Out[49]: [9, 115, 148, 197, 208, 243]

您可以看到,当使用 sorted 时,为了排序目的,值被迭代并像 Python 整数一样处理,然后放回新的 bytearray

但是第 47-48 行的就地排序显示字节被解释为有符号整数,并按其 2 的补码值排序,将数字 >= 128,因为它们是负数,朝向离开了。

我可以通过 运行 在整个 0-255 范围内确认:

In [50]: byt = bytearray(range(0,256))

In [51]: b.heap_sort(byt)

In [52]: list(byt)
Out[52]: 
[128,
 129,
 130,
 131,
 132,
 133,
 134,
 135,
 136,
 137,
 138,
 139,
 140,
 141,
 142,
 143,
 144,
 145,
 146,
 147,
 148,
 149,
 150,
 151,
 152,
 153,
 154,
 155,
 156,
 157,
 158,
 159,
 160,
 161,
 162,
 163,
 164,
 165,
 166,
 167,
 168,
 169,
 170,
 171,
 172,
 173,
 174,
 175,
 176,
 177,
 178,
 179,
 180,
 181,
 182,
 183,
 184,
 185,
 186,
 187,
 188,
 189,
 190,
 191,
 192,
 193,
 194,
 195,
 196,
 197,
 198,
 199,
 200,
 201,
 202,
 203,
 204,
 205,
 206,
 207,
 208,
 209,
 210,
 211,
 212,
 213,
 214,
 215,
 216,
 217,
 218,
 219,
 220,
 221,
 222,
 223,
 224,
 225,
 226,
 227,
 228,
 229,
 230,
 231,
 232,
 233,
 234,
 235,
 236,
 237,
 238,
 239,
 240,
 241,
 242,
 243,
 244,
 245,
 246,
 247,
 248,
 249,
 250,
 251,
 252,
 253,
 254,
 255,
 0,
 1,
 2,
 3,
 4,
 5,
 6,
 7,
 8,
 9,
 10,
 11,
 12,
 13,
 14,
 15,
 16,
 17,
 18,
 19,
 20,
 21,
 22,
 23,
 24,
 25,
 26,
 27,
 28,
 29,
 30,
 31,
 32,
 33,
 34,
 35,
 36,
 37,
 38,
 39,
 40,
 41,
 42,
 43,
 44,
 45,
 46,
 47,
 48,
 49,
 50,
 51,
 52,
 53,
 54,
 55,
 56,
 57,
 58,
 59,
 60,
 61,
 62,
 63,
 64,
 65,
 66,
 67,
 68,
 69,
 70,
 71,
 72,
 73,
 74,
 75,
 76,
 77,
 78,
 79,
 80,
 81,
 82,
 83,
 84,
 85,
 86,
 87,
 88,
 89,
 90,
 91,
 92,
 93,
 94,
 95,
 96,
 97,
 98,
 99,
 100,
 101,
 102,
 103,
 104,
 105,
 106,
 107,
 108,
 109,
 110,
 111,
 112,
 113,
 114,
 115,
 116,
 117,
 118,
 119,
 120,
 121,
 122,
 123,
 124,
 125,
 126,
 127]

我知道这很难重现。如果需要,您可以使用 Cython 构建链接包,然后 import src.buffersort as b 获得我正在使用的相同排序功能。

我尝试通读 Objects/bytearrayobject.c 中 bytearray 的源代码,但我看到一些对 long 的引用和对 PyInt_FromLong 的一些调用...

这让我怀疑 bytearray 的底层 C 级数据在 C 中表示为带符号的整数,但是从原始字节转换为 Python int 意味着在 Python 中,它在 0 到 255 之间是无符号的。我只能假设这是真的......虽然我不明白为什么 Python 应该将 C long 解释为无符号的,除非这只是 bytearray 的约定,我在代码。但如果是这样,如果字节总是被 Python 视为无符号,为什么 C 端也不会使用无符号整数?

如果为真,就地排序的 "right" 结果应该是什么?因为它们 "just bytes" 两种解释都是有效的,我猜,但本着 Python 的精神,我认为它们应该是一种被认为是标准的方式。

为了匹配 sorted 的输出,在处理 bytearray 时,在 C 端将值转换为 unsigned long 是否足够?

Does Python bytearray use signed integers in the C representation?

它使用 chars。这些是否被签名取决于编译器。您可以在 Include/bytearrayobject.h 中看到这一点。 Here's the 2.7 version:

/* Object layout */
typedef struct {
    PyObject_VAR_HEAD
    /* XXX(nnorwitz): should ob_exports be Py_ssize_t? */
    int ob_exports; /* how many buffer exports */
    Py_ssize_t ob_alloc; /* How many bytes allocated */
    char *ob_bytes;
} PyByteArrayObject;

here's the 3.5 version:

typedef struct {
    PyObject_VAR_HEAD
    Py_ssize_t ob_alloc; /* How many bytes allocated in ob_bytes */
    char *ob_bytes;      /* Physical backing buffer */
    char *ob_start;      /* Logical start inside ob_bytes */
    /* XXX(nnorwitz): should ob_exports be Py_ssize_t? */
    int ob_exports;      /* How many buffer exports */
} PyByteArrayObject;

If true, what should be considered the "right" result of the in-place sort?

A Python bytearray 表示 0 <= elem < 256 范围内的整数序列,无论编译器是否认为 char 是有符号的。您可能应该将其排序为 0 <= elem < 256 范围内的整数序列,而不是作为带符号 chars.

的序列

To match output of sorted, will it be sufficient on the C side to cast values to unsigned long when dealing with bytearray?

我对 Cython 的了解还不够多,无法说明正确的代码更改是什么。