直接用 np.zeros() 或 np.ones() 创建超大数组会爆内存或卡死,因其需一次性分配连续内存;改用 np.memmap 可映射磁盘文件按需加载,节省内存且语法兼容;稀疏场景应优先选 scipy.sparse。

为什么直接用 np.zeros() 或 np.ones() 创建超大数组会卡死或爆内存
不是函数本身慢,而是它默认在内存中一次性分配完整块——比如 np.zeros((100000, 100000), dtype=np.float64) 需要约 74.5 GB 连续内存。操作系统无法满足时,Python 会抛出 MemoryError,或者触发系统级 swap,导致整个机器假死。
- 即使你有 128GB 物理内存,NumPy 仍可能因找不到足够连续虚拟地址空间而失败(尤其在 32 位 Python 或某些 Windows 环境)
-
dtype影响巨大:用np.float32能省一半空间,np.uint8再省七倍 - 某些场景根本不需要全量数据驻留内存——比如只做分块计算、流式写入磁盘、或仅访问稀疏区域
用 np.memmap() 创建“伪内存”数组(真正实用的方案)
np.memmap 不把数据全加载进 RAM,而是映射到磁盘文件,读写时按需换页。适合 >1GB 的数组,且支持所有 NumPy 操作(切片、广播、ufunc),语法几乎无感迁移。
- 创建后立即返回对象,不占实际内存:
arr = np.memmap('big.dat', dtype='float32', mode='w+', shape=(200000, 200000)) -
mode='w+'表示可读可写;首次创建时会生成对应大小的空文件(注意磁盘空间是否充足) - 后续可像普通数组一样操作:
arr[0, :] = np.random.rand(200000).astype(np.float32)—— 这行只写入第一行,不加载其余部分 - 务必显式调用
del arr或让变量超出作用域,否则文件句柄可能被锁住,导致下次无法打开
避免踩坑:np.memmap 的几个硬限制和绕过方式
它不是万能的——不能用于多进程共享(每个进程会打开独立文件视图),也不支持某些高级索引(如布尔索引跨块时性能骤降)。更关键的是,它要求文件路径可写、磁盘有足够空间、且 shape 和 dtype 一旦创建就不能改。
- 如果需要动态扩容,先预估最大尺寸创建 memmap,再用
arr[:n, :m]逻辑上“截取”使用区域 - 多进程写入必须加锁(如用
multiprocessing.Lock),或改用zarr/hdf5等支持并发的格式 - Windows 下路径含中文或空格可能出错,统一用英文路径 +
os.path.abspath()规范化 - 调试时别用
print(arr)—— 它会试图加载全部数据,直接崩
真·超大但稀疏?优先考虑 scipy.sparse 而不是 NumPy
如果你的数组大部分是 0(比如推荐系统用户-物品矩阵、图邻接矩阵),用稠密 np.memmap 仍是浪费——10 亿元素里只有 10 万个非零值,却仍要分配 10 亿个槽位。
- 选
scipy.sparse.csr_matrix或csc_matrix:只存非零值 + 行/列索引,内存占用常低于 1% - 支持大部分 NumPy 运算(
@矩阵乘、.sum()),但不支持广播或 fancy indexing - 构造时用三元组:
scipy.sparse.csr_matrix((data, (row, col)), shape=(N, M)),比逐个赋值快几个数量级 - 导出为 memmap 可用
.toarray().tofile(),但通常没必要——稀疏结构本身已是最优表示
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











