memoryview能零拷贝操作但需严格满足条件:仅支持缓冲协议的对象(如bytearray、bytes、array.array、特定numpy.ndarray)可创建,str等不支持;切片越界静默返回空视图,须主动校验长度;仅socket.send、struct.unpack_from等明确支持缓冲协议的接口才真正零拷贝,hashlib.update等需转bytes触发拷贝;可写性取决于原对象(bytes只读,bytearray可写),且视图生命周期不得长于原对象。

memoryview 确实能帮你避免拷贝,但前提是用对对象、用对场景、用对接口——它不是“套上就快”,而是“用错就慢甚至出错”。
传什么对象才能创建有效的 memoryview
必须传支持缓冲协议(buffer protocol)的可变或不可变二进制对象,常见合法输入只有这几个:
-
bytearray(最推荐:可读可写,生命周期易控) -
bytes(只读,适合解析阶段) -
array.array('B')或array.array('I')等字节级类型 -
numpy.ndarray(dtype 必须是字节兼容型,如np.uint8)
传 str 会直接报 TypeError: memoryview: a bytes-like object is required, not 'str';传普通 list 或 dict 会报 TypeError: memoryview: does not have the buffer interface。别试图绕过类型检查——它拦住的是根本不可行的操作。
memoryview 切片为什么没报错但数据不对?
切片如 mv[1000:] 返回空视图,不抛异常;而 mv[1000] 才触发 IndexError。这意味着越界静默失败,极易埋下逻辑 bug。
- 务必在切片前确认
len(mv) >= start + length,尤其在网络包解析或文件头读取时 - 用
.nbytes比len()更可靠(len()对多维视图可能返回第一维长度) - 避免链式切片:如
mv[10:100][20:30]—— 第二层切片仍基于原始内存,但可读性差且易算错偏移
哪些函数接受 memoryview 而不强制拷贝?
只有明确支持缓冲协议的底层接口才能真正零拷贝。不是所有“能接收 bytes 的地方”都认 memoryview。
立即学习“Python免费学习笔记(深入)”;
- ✅
socket.send(mv)、os.write(fd, mv)、struct.unpack_from(fmt, mv, offset)—— 直接走 C 层 buffer 接口 - ❌
hashlib.md5().update(mv)报TypeError;必须写成.update(bytes(mv)),这一步就触发完整拷贝 - ⚠️
json.dumps()、print(mv)、str(mv)都不适用——它们需要 Python 对象语义,memoryview只能转成bytes或list后再用
关键判断:查该函数 CPython 源码或文档是否写着 “accepts a buffer object” 或 “supports the buffer protocol”。不确定就测——用 tracemalloc 或 psutil.Process().memory_info().rss 对比前后内存增长。
为什么 memoryview 改了原对象,但有时又改不动?
可写性取决于原始对象本身:bytes 不可变 → memoryview 只读;bytearray 可变 → memoryview 可写。但更隐蔽的问题是生命周期。
- 一旦原始
bytearray被del或函数返回后局部变量被回收,对应memoryview就变成悬空指针 - 此时读可能返回旧值或随机字节,写可能触发
ValueError: underlying buffer is not writable或静默破坏其他内存 - 典型陷阱:把
memoryview存进类属性或全局 dict,却让构造它的bytearray在函数退出后即销毁
没有运行时 API 能检测视图是否有效——唯一靠谱的做法是确保原对象存活时间 ≥ 所有视图的整个使用周期,必要时用 weakref.ref 做守卫,但代价是额外开销。


















