memoryview 用于高效切片、类型转换和传输二进制数据的零拷贝视图 C 扩展;适用于 bytes、bytearray、array.array、numpy.ndarray 不能用于支持缓冲区协议的对象 list 或 str;确保源对象的生命周期比源对象长 memoryview,而且写作操作只在源可写时生效。memoryview 它是什么,什么时候应该用它?
memoryview 它不是数组或容器,而是现有的二进制数据(例如 bytes、bytearray、array.array、numpy.ndarray)“零拷贝视图”。它允许你直接读写底层内存块,而不复制原始数据——这对大数组(GB 等级)至关重要。但是注意:memoryview 没有数据本身,原对象被销毁后,memoryview 失效(会抛) ValueError: memoryview has been closed)。
- 适用场景:需要频繁切片、类型转换或传输 C 扩展(如 NumPy、Pillow、struct)只是不想触发深拷贝
- 不适用场景:想要修改不可变物体(例如)
bytes)的内容——memoryview可读,但写会报TypeError - 常见误用:普通
list或str构造memoryview→ 直接报TypeError: cannot make memory view from a list
最安全的起点是 array.array,支持可变缓冲区协议,跨平台兼容性好。使用 array.array('d', [...]) 创建双精度浮点数组后,直接传输给 memoryview:
import array
data = array.array('d', [1.0, 2.0, 3.0, 4.0])
mv = memoryview(data)这时 mv 和 data 共享同一块内存。你可以:
- 不复制切片:
mv[1:3]返回新memoryview,它仍然指向原始内存 - 类型重解释(要求元素大小相同):
mv.cast('B')把 float64 数组转换为字节视图(8)×4=32 字节),mv.cast('I')转为 uint32(只有当原型是整形的,对齐的时候才安全) - 写作必须确保目标可以写:
data可以写,所以mv[0] = 99.0会真实修改data[0];但若用memoryview(b'hello'),赋值会失败
NumPy 数组的 .data 属性返回 memoryview,但这是只读的(除非 ndarray.flags.writeable = True)。更推荐用 np.asarray(mv) 或 np.frombuffer(mv, dtype=...) 构造新数组(仍不复制):
立即学习“Python免费学习笔记(深入);
Python 3.14.2
Python 3.14.2是Python编程语言于2025年12月5日发布的稳定版本,属于3.14系列的第二次维护更新。该版本包含18个修复项目,重点解决多过程、数据和正则表达模块的回归问题,修复CVE-2025-12084等安全漏洞。这个版本标志着Python发展的一个重要里程碑,即自由线程模式(删除GIL)正式得到官方支持。
下载import numpy as np mv = memoryview(bytearray(range(1000))) arr = np.frombuffer(mv, dtype=np.uint8) # 共享内存,arr[0] = 255 同时改 mv[0]
但要注意:
-
np.frombuffer()要求memoryview对应的 buffer 是连续的(mv.contiguous为True),否则报ValueError: buffer is not contiguous - NumPy 的
ndarray默认启用写作保护,如果原始保护memoryview来自只读源(如 mmap 即使设置了文件)writeable=True也会在写入的时候 segfault - 避免混合使用:
np.array(mv)会被迫复制-这是最常踩的坑
比如读一个 2GB 的二进制 float32 数据文件,逐块处理,不完全加载到内存:
with open('data.bin', 'rb') as f:
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
mv = memoryview(mm)
# 按每 100 万个 float32(4MB)切割处理
for i in range(0, len(mv), 1000000 * 4):
chunk_mv = mv[i:i + 1000000 * 4]
arr = np.frombuffer(chunk_mv, dtype=np.float32)
process(arr) # 此时 arr 与文件内存共享,无副本关键点:
- 必须用
mmap或bytearray这种支持缓冲区协议的对象作为源头 - 每次
frombuffer显式指定必须显式指定dtype,不能依赖mv.format(可能只是'B') - 处理后及时释放
mmap,否则,文件的句柄泄漏;memoryview本身不需要手动 close,但是,当所附对象被销毁后,它将失效
真正困难的是控制生命周期——一旦你把它放在一边 memoryview 谁负责保存多个函数的原始? buffer,中间的某个地方很容易发生意外 del 掉源对象,然后下一行 crash。这比语法细节更值得花时间设计。