在将一些旧的python 2代码迁移到python 3时,我遇到了一些问题,即从字节对象填充结构化的numpy数组。
我有一个解析器,为我可能遇到的每种数据结构类型定义了一个特定的dtype。通常,由于给定的数据结构可能具有可变长度或可变类型的字段,因此这些字段已在numpy数组中表示为对象dtype(np.object #alternatively np.dtype('O')
)的字段。
该数组是bytearray
通过首先填充fixed-dtype字段从字节(或a )获得的。此后,可以使用前面的固定字段中的信息来构建任何子数组(包含在“对象”字段中)的dtype。
这是在python 2中工作的此过程的一部分示例(仅处理fixed-dtype字段)。请注意,我们有一个名为的字段'nSamples'
,大概可以告诉我们该数组的'samples'
字段所指向的数组的长度,这将被解释为具有shape(2,)
和dtype的numpy数组sampleDtype
:
fancyDtype = np.dtype([('blah', '<u4'),
('bleh', 'S5'),
('nSamples', '<u8'),
('samples', 'O')])
sampleDtype = np.dtype([('sampleId', '<u2'),
('val', '<f4')])
bytesFromFile = bytearray(
b'*\x00\x00\x00hello\x02\x00\x00\x00\x00\x00\x00\x00\xd0\xb5'
b'\x14_\xa1\x7f\x00\x00"\x00\x00\x00\x80?]\x00\x00\x00\xa0@')
arr = np.zeros((1,), dtype=fancyDtype)
numBytesFixedPortion = 17
# Start out by just reading the fixed-type portion of the array
arr.data[:numBytesFixedPortion] = bytesFromFile[:numBytesFixedPortion]
memoryview(arr.data)[:numBytesFixedPortion] = bytesFromFile[:numBytesFixedPortion]
最后两个语句都可以在python 2.7中使用。
值得注意的是,如果我输入
arr.data
我得到了<read-write buffer for 0x7f7a93bb7080, size 25, offset 0 at 0x7f7a9339cf70>
,这告诉我这是一个缓冲区。显然,memoryview(arr.data)
返回一个memoryview
对象。
这两个语句在python 3.6中引发以下异常:
NotImplementedError: memoryview: unsupported format T{I:blah:5s:bleh:=Q:nSamples:O:samples:}
这告诉我numpy返回的data
属性访问类型是amemoryview
而不是a buffer
。它还告诉我,它memoryviews
在python 2.7中有效,但在python 3.6中却不行。
我在numpy的问题跟踪器中发现了一个类似的问题:https : //github.com/numpy/numpy/issues/13617但是,该问题很快被关闭,numpy开发人员指出这是的错误ctypes
。由于ctypes
是内置的,我有点希望仅对其进行更新以得到修复。
我最终确实偶然发现了一个可行的解决方案,尽管它花费的时间大约是python 2.7方法的两倍。它是:
import struct
struct.pack_into(
'B' * numBytesFixedPortion, # fmt
arr.data, # buffer
0, # offset
*buf[:numBytesFixedPortion] # unpacked byte values
)
一位同事还建议尝试使用此解决方案:
arrView = arr.view('u1')
arrView[:numBytesFixedPortion] = buf[:numBytesFixedPortion]
但是,在执行此操作时,出现异常:
File "/home/tintedFrantic/anaconda2/envs/py3/lib/python3.6/site-packages/numpy/core/_internal.py", line 461, in _view_is_safe
raise TypeError("Cannot change data-type for object array.")
TypeError: Cannot change data-type for object array.
请注意,我在python 2.7和3.6中都遇到了此异常。似乎numpy不允许在具有任何object
字段的阵列上进行查看。(此外:通过注释掉numpy代码中对对象类型字段的检查,我能够使numpy正确地做到这一点,尽管这似乎是一个危险的解决方案(也不是一种非常可移植的解决方案))。
我也尝试过创建单独的数组,一个数组带有fixed-dtype字段,另一个数组带有object-dtype字段,然后numpy.lib.recfunctions.merge_arrays
用于合并它们。失败的原因是我不记得一条神秘的消息。
我有点茫然。我只想向numpy数组的基础内存写入一些任意字节,然后高效地进行操作。这似乎并不难,但我还没有找到一种好的方法。我想要一个也不是hack的解决方案,因为这将进入需要高可靠性的系统。如果没有更好的struct.pack_into()
办法,我将使用该解决方案,但我希望那里的人知道更好的办法。顺便说一句,不使用object-dtype字段不是可行的选择,因为这样做的成本太高了。
如果重要的话,我正在python 2.7中使用numpy 1.16.2,对于python 3.6使用1.17.4。
根据@nawsleahcimnoraa的建议,我发现在python 3.3+中(而不是在python 2.7中),在我的python 3环境中memoryview
返回的对象arr.data
有一个cast()
方法。因此,我可以
arr.data.cast('B')[startIdx:endIdx] = buf[:numBytes]
这更像我在python 2.7中的样子。它比struct
上面的方法简洁得多,并且性能也更好。
我在测试这些解决方案时注意到的一件事是,通常,python 3解决方案比python 2版本慢。例如,我struct
同时使用python 2和python 3尝试了该解决方案,发现python 3的处理时间显着增加。
我还发现相同版本的不同python环境之间存在相当大的差异。例如,我发现python 3.6的系统安装比python 3.6的虚拟环境安装表现更好,因此看来结果可能很大程度上取决于给定环境的配置。
总的来说,我对使用cast()
返回的memoryview对象的方法的结果感到满意,arr.data
并将暂时使用它。但是,如果有人发现更好的方法,我仍然很想听听。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句