从NumPy数组创建Python bytearray时,额外的数据来自哪里?

从NumPy数组创建Python bytearray时,额外的数据来自哪里?,python,numpy,buffer,bytearray,protocol-buffers,Python,Numpy,Buffer,Bytearray,Protocol Buffers,考虑两种简单地使用bytearray制作相同的bytearray(使用Python 2.7.11,但在3.4.3中也确认了相同的行为): 由于array.array和numpy.ndarray都支持缓冲区协议,因此我希望两者在转换为bytearray时导出相同的底层数据 但是上面的数据: In [86]: b1 Out[86]: bytearray(b'\x01\x03\x02\x05\x04') In [87]: b2 Out[87]: bytearray(b'\x01\x00\x00\x0

考虑两种简单地使用bytearray制作相同的
bytearray
(使用Python 2.7.11,但在3.4.3中也确认了相同的行为):

由于
array.array
numpy.ndarray
都支持缓冲区协议,因此我希望两者在转换为
bytearray
时导出相同的底层数据

但是上面的数据:

In [86]: b1
Out[86]: bytearray(b'\x01\x03\x02\x05\x04')

In [87]: b2
Out[87]: bytearray(b'\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00')
起初,我认为在NumPy数组上对
bytearray
的简单调用可能会由于数据类型、连续性或其他开销数据而无意中获得一些额外的字节

但即使直接查看NumPy缓冲区数据句柄,它仍然表示大小为40,并给出相同的数据:

In [90]: a2.data
Out[90]: <read-write buffer for 0x7fb85d60fee0, size 40, offset 0 at 0x7fb85d668fb0>

In [91]: bytearray(a2.data)
Out[91]: bytearray(b'\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00')
我注意到,如果我给出
dtype=np.int32
,那么
bytearray(a2)
的长度是20而不是40,这表明额外的字节与类型信息有关——只是不清楚为什么或者如何:

In [20]: a2 = np.asarray([1,3,2,5,4], dtype=int)

In [21]: len(bytearray(a2.data))
Out[21]: 40

In [22]: a2 = np.asarray([1,3,2,5,4], dtype=np.int32)

In [23]: len(bytearray(a2.data))
Out[23]: 20
一个aict,
np.int32
应该对应于
数组
'L'
类型代码,但是任何关于为什么不这样的解释都会非常有用


如何可靠地只提取“应该”通过缓冲协议导出的部分数据。。。与中一样,与本例中的普通
数组
数据的外观相同。

当您从
数组.array
创建bytearray时,它将其视为整数的可替代项,而不是缓冲区。您可以看到这一点,因为:

>>> bytearray(a1)
bytearray(b'\x01\x03\x02\x05\x04')
>>> bytearray(buffer(a1))
bytearray(b'\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00')
也就是说,直接从数组创建bytearray可以得到“普通”整数,但从数组的缓冲区创建bytearray可以得到这些整数的实际字节表示形式。此外,您不能从包含不适合单个字节的整数的数组中创建bytearray:

>>> bytearray(array.array(b'L', [256]))
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    bytearray(array.array(b'L', [256]))
ValueError: byte must be in range(0, 256)
bytearray(array.array(b'L',[256])) 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 bytearray(array.array(b'L',[256])) ValueError:字节必须在范围(0,256)内 尽管如此,这种行为仍然令人费解,因为
array.array
np.ndarray
都支持缓冲协议和迭代,但是从
array.array
以某种方式创建bytearray通过迭代获取数据,从
numpy.ndarray
创建字节数组时,通过缓冲协议获取数据。在这两种类型的C内部,可能有一些神秘的解释来解释这种切换优先级,但我不知道它是什么


在任何情况下,说你用
a1
看到的是“应该”发生的事情都是不正确的;如上所示,数据
'\x01\x03\x02\x05\x04'
实际上不是
array.array
通过缓冲协议公开的内容。如果有的话,numpy数组的行为就是您“应该”从缓冲协议中得到的;这是
array.array
行为与缓冲区协议不一致。

我在这两种情况下得到相同的bytearray:

In [1032]: sys.version
Out[1032]: '3.4.3 (default, Mar 26 2015, 22:07:01) \n[GCC 4.9.2]'
In [1033]: from array import array

In [1034]: a1=array('L',[1,3,2,5,4])
In [1035]: a2=np.array([1,3,2,5,4],dtype=np.int32)

In [1036]: bytearray(a1)
Out[1036]: bytearray(b'\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00')
In [1037]: bytearray(a2)
Out[1037]: bytearray(b'\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00')
在这两种情况下,我都有5个数字,每个数字占用4个字节(作为32位整数)-20个字节

bytearray
可能要求使用以下方法(或类似方法):

我可以通过更改数据类型来删除额外的字节:

In [1059]: a2.astype('i1').tostring()
Out[1059]: b'\x01\x03\x02\x05\x04'

从1.6版开始,Python一直提供Python级缓冲区对象和C级缓冲区API,以便任何内置或使用的定义类型都可以公开其特性。然而,由于各种缺点,这两种方法都被弃用,并在Python 3.0中被正式删除,取而代之的是新的C级缓冲区API和新的Python级对象memoryview

新的缓冲区API已后端口到Python2.6,memoryview对象已后端口到Python2.7。强烈建议您使用它们而不是旧的API,除非出于兼容性原因您被阻止这样做


考虑到缓冲区接口中的这些更改,旧的
数组
模块在2.6和2.7中没有更改,而是在3.0+中更改,这并不奇怪。

您所说的“应该”是什么意思?缓冲协议只是指定如何获取数据,它没有说明数据应该是什么。这是因为numpy默认为64位(16个半字节)?尝试更改字节顺序(big-endian,little-endian),看看会发生什么。看到了吗?迭代
numpy.ndarray
会产生数组的数据类型的标量,而任何整数类型的
array.array
会产生
int
值,因此会产生不同的行为。我更多的意思是,一种行为或另一种行为应该是“预期的”方式。至少令人惊讶的是,对于这两种类型,对bytearray的简单调用不同并不好。不过,如果这两种行为中的任何一种是预期的违约行为,我会很高兴。在我的应用程序中,这只意味着在处理
数组时,我需要小心陷入一堆
缓冲区
调用。array
@Goyo当我使用
int
作为numpy数据类型时会发生什么情况呢。。为什么在两个numpy情况下总字节长度是20和40。看起来更多的是关于数组的简单故事。数组int根据需要提供尽可能少的字节(即1),而numpy总是提供4个字节。。。这似乎并没有发生。@Goyo:这仍然不能解释,因为
bytearray([np.int32(x)表示1,2,3中的x])
仍然返回一个带有“普通”int值的bytearray,而不像
bytearray(np.array([1,2,3],dtype=np.int32))
。所以这不仅仅是单个值的问题。@F先生:当您指定
int
作为数据类型时,numpy只选择一个平台默认的numpy整数类型,在您的例子中显然是
int64
。您可以检查结果数组的数据类型,以查看其数据类型实际上是什么。您是正确的。我没有重新检查Python3中的
array.array
示例,只有NumPy示例。这与t的方式有关
In [1032]: sys.version
Out[1032]: '3.4.3 (default, Mar 26 2015, 22:07:01) \n[GCC 4.9.2]'
In [1033]: from array import array

In [1034]: a1=array('L',[1,3,2,5,4])
In [1035]: a2=np.array([1,3,2,5,4],dtype=np.int32)

In [1036]: bytearray(a1)
Out[1036]: bytearray(b'\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00')
In [1037]: bytearray(a2)
Out[1037]: bytearray(b'\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00')
In [1038]: a1.tobytes()
Out[1038]: b'\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00'
In [1039]: a2.tostring()
Out[1039]: b'\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00'
In [1059]: a2.astype('i1').tostring()
Out[1059]: b'\x01\x03\x02\x05\x04'