np.memmap可实现磁盘级分页,按需加载数据块以避免内存溢出;需显式指定dtype、shape等参数对齐字节偏移,并配合分块迭代、合适数据类型与内存布局及生成器优化内存使用。

为什么np.array()或np.zeros()会突然抛出MemoryError
不是数据量“看起来不大”,就一定不会爆内存。NumPy数组在创建时直接申请连续内存块,实际占用 = 元素个数 × 单个元素字节大小。比如 np.zeros((10000, 10000), dtype=np.float64) 需要约 745MB(10⁸ × 8 字节),而 dtype=np.float32 只需约 373MB;若误用 np.float64 处理本可压缩的整数型数据,内存翻倍是常态。
常见诱因包括:pd.read_csv() 默认推断为 float64,后续转 np.array() 时未降精度;图像处理中加载多张 uint8 图却存为 float64;循环中不断 np.concatenate() 拼接新数组,旧数组未释放。
如何用dtype参数立即减半内存占用
90% 的 MemoryError 可通过显式指定更小的 dtype 解决,而不是靠“等机器升级”。关键不是“能不能算”,而是“要不要存那么高精度”。
- 整数范围明确(如像素值 0–255)→ 用
np.uint8或np.int16,别用默认int64 - 浮点计算不需双精度 → 改用
np.float32,尤其在深度学习输入、图像归一化后 - 布尔掩码 → 用
np.bool_(注意:不是 Pythonbool,后者在 NumPy 中占 8 字节) - 读取 CSV 时直接限定类型:
pd.read_csv(..., dtype={'col': 'uint8'}),再转np.array(..., dtype=np.uint8)
np.memmap() 是什么?什么时候必须用它
当数组大到无法全载入内存(比如 >2GB 的遥感影像、基因序列),又必须像普通数组一样索引、切片、参与计算时,np.memmap() 是唯一实用解法——它把磁盘文件映射为数组接口,只将访问到的页加载进内存。
立即学习“Python免费学习笔记(深入)”;
实操要点:
- 创建前确保磁盘有足够空间,且路径可写:
np.memmap('data.dat', dtype='float32', mode='w+', shape=(1000000, 1000)) -
mode='r'仅读,'r+'可读写已存在文件,'w+'覆盖新建(慎用) - 不能直接对 memmap 对象调
np.sort()等全量操作——会尝试加载全部数据;改用分块处理:for i in range(0, arr.shape[0], 10000): chunk = arr[i:i+10000]; process(chunk) - 注意:
np.memmap不自动释放磁盘文件,退出前建议del arr并手动os.remove('data.dat')(如果不再需要)
避免隐式复制:警惕copy=True和链式操作
很多 NumPy 函数默认生成新数组,叠加调用会成倍吃内存。例如 arr.astype(np.float32).T.reshape(-1).mean() 中,每一步都产生完整副本。
- 能原地修改就别复制:
arr.astype(np.float32, copy=False)(仅当源 dtype 可安全转换时有效) - 转置、reshape 等视图操作本身不复制,但接在
.astype()后面就会触发复制——把.T和.reshape()提前到类型转换前 - 用
np.nditer或numba.jit替代广播运算,尤其在逐元素条件赋值场景(如arr[arr > 0.5] = 1在大数组上可能临时分配布尔掩码) - 检查中间变量:运行
arr.nbytes确认实际大小;用psutil.Process().memory_info().rss监控进程内存变化
真正棘手的从来不是“怎么让代码跑起来”,而是“哪一行悄悄把 10GB 数据复制了三份”。盯住 .nbytes,比调参重要得多。


















