一、memoryview是什么?
1、memoryview的概念
在Python中,memoryview是一个在不复制内容的情况下访问内存的工具。它是一个内存切片的概念,它允许我们在不复制数据的情况下访问和操作二进制数据。
2、memoryview的定义方法
# 定义方法 mv = memoryview(byte_array)
在上面的代码中,byte_array可以是bytes对象或bytearray对象。通过memoryview实例化一个对象后,它将提供一些方法来访问底层数据,例如mv[i]和mv[i:j](切片)。
3、memoryview的内存模型
创建一个memoryview对象时,Python存储器资源是以numpyndarray的方式进行管理的。它实际上是两个标准C结构的Python对象:
- Py_buffer(也称为Py_buffer结构体),它定义了Numpy的ndarray存储器结构,并描述了访问这个存储器需要的所有信息。
- MemoryViewObject,实质上是Python内部的一个主语义对象,它实现了一个memoryview对象实例。
二、memoryview在Python中有哪些用途?
1、memoryview优势
对于Python内置的数组数据类型(如NDArray和PyArrayObject)来说,memoryview的一个显著优点是它可以利用Python的GIL(全局解释器锁)被并行修改,而不会破坏数据的完整性和一致性。
2、memoryview的用途
- Memoryview可以提高Python程序执行效率,因为它能够比较整块地存储和处理二进制数据。
- Memoryview特别适用于块处理文件或网络数据流。
- Memoryview对于多个并发任务处理二进制数据也特别有用,可以实现数据的并行访问而无需加锁。
三、常用的memoryview操作方法
1、memoryview选取
memoryview允许我们像操作普通的Python序列(例如list和tuple)一样进行切片操作。但是,与普通的Python序列不同的是,memoryview的切片并不会创建新的数据副本,而是直接返回底层数据的一个视图。
# 创建bytearray bytearray_test = bytearray(b'abcd') # 通过memoryview选取子对象 mv_test = memoryview(bytearray_test) sub_mv_test = mv_test[1:3] print(sub_mv_test) ## 修改对象 sub_mv_test[:] = b'xy' print(mv_test.tobytes()) # b'axyd'
2、memoryview.cast
通过memoryview对象可以提供对同一个内存区域的不同切片视图,还可以使用memoryview.cast方法创建一个对象的内存视图。
import numpy as np # 创建一个int8数组 array_test = np.zeros((3,), dtype=np.int8) array_test_pointer = memoryview(array_test) # 将int8数组地址转换成int32数组地址,并创建memoryview int32_array_pointer = array_test_pointer.cast('i') print(int32_array_pointer.format) # 'i' print(int32_array_pointer.itemsize) # 4 print(int32_array_pointer.shape) # (3,)
3、memoryview.bytes_per_element
bytes_per_element是memoryview对象的一个属性,返回单个元素的大小(以字节为单位)。
array_bytes = bytearray(b'123456') array_pointer = memoryview(array_bytes) print(array_pointer.bytes_per_element) # 1
四、memoryview与USB设备交互的示例
下面将演示如何利用memoryview和PyUSB连接USB设备,读取USB设备返回的二进制数据并进行处理。
import usb.core import usb.util # 查找USB设备 device = usb.core.find(idVendor=0x1234, idProduct=0x5678) # 设置配置并进行USB设备输出流的连接 if device is not None: endpoint_out = device[0][(0, 0)][0] device.write(endpoint_out, 'test') # 读取USB设备返回的二进制数据 endpoint_in = device[0][(0, 0)][1] device.read(endpoint_in.bEndpointAddress, endpoint_in.wMaxPacketSize) # 利用memoryview处理返回的二进制数据 response_mv = memoryview(device.read(endpoint_in.bEndpointAddress, endpoint_in.wMaxPacketSize))
五、memoryview的局限性
1、不能被序列化
memoryview对象不能被Python pickle序列化,因为它们是包含底层内存地址和长度信息的指针。这意味着如果需要序列化,必须在调用pickle模块之前先转换为bytes或bytearray类型。
2、不能跨进程使用
由于memoryview基于底层内存视图的概念,因此它们通常只对包含它们的那个Python程序实例可用,而且不能被另一个Python程序实例共享。这也是因为操作系统的Kernal通常不允许一个进程访问另一个进程的内存。
3、不适用于所有二进制数据类型
虽然memoryview可以处理多种类型的二进制数据(例如bytes对象和numpy数组),但是对于C结构之类的复杂类型,memoryview可能不是一个良好的选择。这是因为指针操作和内存布局可能存在非常细微的差异,这将使得memoryview无法精确地处理它。
六、结语
通过本文对memoryview的深入剖析,我们了解了它的概念、优势、用途以及实际应用示例,同时也揭示了它的局限性。对于经常需要处理二进制数据的Python开发人员来说,memoryview无疑是一个非常有用的工具。