考虑两种天真地制作相同bytearray
的方法(使用Python 2.7.11,但在3.4.3中也确认了相同的行为):
In [80]: from array import array
In [81]: import numpy as np
In [82]: a1 = array('L', [1, 3, 2, 5, 4])
In [83]: a2 = np.asarray([1,3,2,5,4], dtype=int)
In [84]: b1 = bytearray(a1)
In [85]: b2 = bytearray(a2)
由于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')
a2.view()
发生同样的失败:
In [93]: bytearray(a2.view())
Out[93]: 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
AFAICT,np.int32
应该与array
'L'
类型代码相对应,但是对于为什么不对其进行任何解释都会有很大的帮助。
如何才能可靠地仅提取数据的一部分&#34;应该&#34;通过缓冲协议导出...就像在这种情况下的普通array
数据一样。
答案 0 :(得分:6)
当您从array.array
创建bytearray时,它会将其视为一个可迭代的int,而不是缓冲区。你可以看到这个因为:
>>> 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)
但是,行为仍然令人费解,因为array.array
和np.ndarray
都支持缓冲协议和迭代,但不知何故创建了来自array.array
的bytearray通过迭代获取数据,而从numpy.ndarray
创建bytearray则通过缓冲协议获取数据。对于这两种类型的C内部,这个切换优先级可能有一些神秘的解释,但我不知道它是什么。
无论如何,说你用a1
看到的是“应该”发生的事情并不正确;如上所示,数据'\x01\x03\x02\x05\x04'
实际上不是array.array
通过缓冲协议公开的内容。如果有的话,numpy数组的行为就是你应该从缓冲区协议中得到的; array.array
行为与缓冲协议不一致。
答案 1 :(得分:3)
我在两种情况下得到相同的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 [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'
我可以通过更改dtype来删除多余的字节:
In [1059]: a2.astype('i1').tostring()
Out[1059]: b'\x01\x03\x02\x05\x04'
https://docs.python.org/2.6/c-api/buffer.html
从版本1.6开始,Python一直在提供Python级缓冲区对象和C级缓冲区API,以便任何内置或使用定义的类型都可以公开其特性。但是,由于存在各种缺点,两者都已被弃用,并且已在Python 3.0中正式删除,以支持新的C级缓冲区API和名为memoryview的新Python级对象。
新的缓冲区API已被反向移植到Python 2.6,并且memoryview对象已被反向移植到Python 2.7。强烈建议使用它们而不是旧的API,除非出于兼容性原因而阻止这样做。
鉴于缓冲区界面中的这些变化,旧的array
模块在2.6和2.7中未更改,但在3.0 +中已更改,这并不奇怪。