首页 > 解决方案 > Numpy数组:在不复制的情况下获取原始字节

问题描述

我正在尝试将多个 Numpy 数组的字节连接成一个bytearray,以便在 HTTP 发布请求中发送它。

我能想到的最有效的方法是创建一个足够大的bytearray对象,然后将所有 numpy 数组中的字节连续写入其中。

代码将如下所示:

list_arr = [np.array([1, 2, 3]), np.array([4, 5, 6])]
total_nb_bytes = sum(a.nbytes for a in list_arr)
cb = bytearray(total_nb_bytes)

# Too Lazy Didn't do: generate list of delimiters and information to decode the concatenated bytes array

# concatenate the bytes
for arr in list_arr:
    _bytes = arr.tobytes()
    cb.extend(_bytes)    

该方法tobytes()不是零拷贝方法。它将 numpy 数组的原始数据复制到一个bytes对象中。

在 python 中,缓冲区允许访问内部原始数据值(这在 C 级别称为协议缓冲区) Python 文档;numpy 在 numpy1.13 中有这种可能性,该方法称为getbuffer() link。然而,这种方法已被弃用!

这样做的正确方法是什么?

标签: pythonarraysnumpyzero-copypython-bytearray

解决方案


您可以从您的消息中创建一个与 numpy 兼容的缓冲区,并使用's参数bytearray有效地写入该缓冲区。np.concatenateout

list_arr = [np.array([1,2,3]), np.array([4,5,6])]
total_nb_bytes = sum(a.nbytes for a in list_arr)
total_size = sum(a.size for a in list_arr)
cb = bytearray(total_nb_bytes)

np.concatenate(list_arr, out=np.ndarray(total_size, dtype=list_arr[0].dtype, buffer=cb))

果然,

>>> cb
bytearray(b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00')

此方法意味着您的输出都是相同的格式。要解决此问题,请将您的原始数组视为np.uint8

np.concatenate([a.view(np.uint8) for a in list_arr],
               out=np.ndarray(total_nb_bytes, dtype=list_arr[0].dtype, buffer=cb))

这样,您也不需要计算total_size,因为您已经计算了字节数。

这种方法可能比遍历数组列表更有效。你是对的,缓冲协议是你解决问题的门票。您可以使用低级np.ndarray构造函数创建一个包裹在任何支持缓冲区协议的对象的内存周围的数组对象。从那里,您可以使用所有常用的 numpy 函数与缓冲区进行交互。


推荐阅读