解决Python3整数数组转bytes的效率问题可以采用两种方式,分别是原生bytes方法和NumPy库的方式。
原生bytes方法
基础方法
- 将整数数组转换成bytes。
使用Python内置函数bytes()可以将整数数组转换为bytes类型,示例如下:
nums = [1, 2, 3, 4]
bytes_data = bytes(nums)
这样就可以将整数数组 [1, 2, 3, 4] 转换为bytes类型。
- 使用struct模块打包成字节流
struct模块可以将Python中的数据类型转化为C中的数据类型,从而实现字节流的打包和解包。其中,pack函数是将数据转化为字节流,unpack函数是将字节流转化为数据。
如对于以下整数数组转换为bytes:
nums = [1, 2, 3, 4, 255]
byte_array = bytearray(struct.pack('>' + 'I' * len(nums), *nums))
其中 ">" 是大端模式(网络字节序),"I" 表示4字节的无符号整型。一定要将所有的nums元素都打包传入。
- 使用memoryview方法
memoryview() 函数返回给定参数的内存查看对象(memoryview对象),这个对象允许Python代码在没有复制内容的情况下访问Python对象的内部数据。
arr = [1, 2, 3, 4, 255]
b = bytes(memoryview(arr).cast('B'))
其中,memoryview() 函数接可写入任何对象,它的 cast() 方法可以强制用指定类型的项构造一个新的 memoryview 对象。
但是,当元素的数值超过256时,会自动模256,因此需要在解析时使用函数bytesub:
def bytesub(byte_array: bytes):
return bytes((x % 256) for x in byte_array)
arr = [1, 2, 3, 4, 255]
b = bytesub(memoryview(arr).cast('B'))
优化
- 在原生bytes方法中,使用bytes()在转换大型数组时会生成临时Python对象。这在大型数组上可能会更慢并且占用更多内存。因此,我们可以使用 bytearray() 消除额外的对象复制:
nums = [1, 2, 3, 4, 255]
b = bytearray()
b.extend(nums)
- 使用数组模块 array,从而避免了字节复制:
import array
nums = [1, 2, 3, 4, 255]
b = array.array('B', nums).tobytes()
NumPy库的方法
对于大型数组进行转换,NumPy库的速度和效率更高。这个NumPy库的主要数据类型是ndarray,是一个高效、多维数组,提供了与 C/C++ 数组的快速访问和转换所需的基本类型的大小和字节序。它可以将整数数组以高效方式转换为 bytes 类型。具体步骤如下:
- 创建一个 NumPy 数组:
import numpy as np
nums = np.array([1, 2, 3, 4, uint8(255)], dtype=np.uint8)
-
其中, dtype=np.uint8 是一个关键点。在计算机界中,编码信息的最小单元是 8 位,所以使用 uint8 就可以明确地表示 0~255,避免了无关的向下取整,同时物尽其用。
-
转换为字节流:
np_data = np.array(nums).tobytes()
或者使用:
np_data = nums.tobytes()
示例
示例1:使用原生bytes方法转换一个8192整数长的数组,使用timeit函数比较字节数量及其对应时间
使用以下方法将8192位整数数组转换为字节流,bytes() 方法生成的临时对象数目 ~ 8192 * 8,可能很耗时。实际经过时间检查,可以看到其花费了大约 0.061秒:
from array import array
import timeit
NUMS = array('L', range(8192));
def bytes_loop():
b = bytearray()
for num in NUMS:
b.append(num & 0xff)
b.append(num >> 8 & 0xff)
b.append(num >> 16 & 0xff)
b.append(num >> 24 & 0xff)
return bytes(b)
def bytes_loop_pruned():
b = bytearray(len(NUMS) * 4)
index = 0
for num in NUMS:
b[index] = num & 0xff
b[index + 1] = num >> 8 & 0xff
b[index + 2] = num >> 16 & 0xff
b[index + 3] = num >> 24 & 0xff
index += 4
return bytes(b)
def bytes_method():
return bytes(NUMS)
N = 1000
print(timeit.timeit(bytes_loop, number=N))
print(timeit.timeit(bytes_loop_pruned, number=N))
print(timeit.timeit(bytes_method, number=N))
结果:
6.922980099391505
1.2628497272736984
0.009422727272909193
示例2:使用 NumPy 库来解决相同的缓慢性能
考虑以下示例:
import numpy as np
from array import array
import timeit
NUMS = np.array(array('L', range(8192)), dtype=np.uint32)
def numpy_method():
return NUMS.tobytes()
def original_bytes_method():
items = list(NUMS)
return bytes(memoryview(items))
N = 1000
print(timeit.timeit(numpy_method, number=N))
print(timeit.timeit(original_bytes_method, number=N))
该代码与前一示例非常相似 - 均针对8192个元素为32位的数组。与原始bytes()方法和array.array()相比,numpy.tobytes()方法使用的实际内存减少了某些“再次分段”的重新分配开销。结果:
0.008493032778605789
9.399150620732546
可以看到在将大量整数转换为字节流的时候,numpy可以比原生bytes方法快得多。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解决python3 整数数组转bytes的效率问题 - Python技术站