You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用memmap模式的np.save与np.load在Windows 11下抛出OSError

问题分析:Windows下numpy内存映射文件写入冲突导致OSError

问题复现

运行以下代码:

import numpy as np
np.save('tmp.npy', np.empty(128))
tmp = np.load('tmp.npy', mmap_mode='r+')
np.save('tmp.npy', tmp[:64])

在Windows 11系统下抛出如下错误:

---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
Cell In[1], line 4
      2 np.save('tmp.npy', np.empty(128))
      3 tmp = np.load('tmp.npy', mmap_mode='r+')
----> 4 np.save('tmp.npy', tmp[:64])

File <__array_function__ internals>:180, in save(*args, **kwargs)

File c:\Users\User\Documents\KULIAH\LAB_SOFTWARE\python_proj\anagrambotid\.venv\lib\site-packages\numpy\lib\npyio.py:518, in save(file, arr, allow_pickle, fix_imports)
    516     if not file.endswith('.npy'):
    517         file = file + '.npy'
--> 518     file_ctx = open(file, "wb")
    520 with file_ctx as fid:
    521     arr = np.asanyarray(arr)

OSError: [Errno 22] Invalid argument: 'tmp.npy'

原因解析

  • Windows与Linux的文件系统锁机制存在差异:Windows对处于内存映射状态的文件会施加独占锁,不允许同一进程内的其他文件句柄以写入模式打开该文件;而Linux的文件锁策略更宽松,允许在内存映射的同时对文件进行写入操作。
  • 当你用mmap_mode='r+'加载tmp.npy后,内存映射对象会持有该文件的锁定,此时调用np.save会尝试以wb(写入二进制)模式重新打开同一个文件,Windows系统直接拒绝这个操作,从而抛出OSError。
  • tmp.flush()仅负责将内存映射中的数据刷回磁盘,但不会释放文件的锁定资源,因此无法解决该冲突。

解决方案

方案1:先释放内存映射锁再保存

先提取需要的切片内容并复制到内存,然后删除内存映射对象以释放文件锁,再执行保存操作:

import numpy as np
np.save('tmp.npy', np.empty(128))
tmp = np.load('tmp.npy', mmap_mode='r+')
# 复制切片内容到内存,脱离内存映射
arr_slice = tmp[:64].copy()
# 删除内存映射对象,释放文件锁定
del tmp
# 此时可以正常保存
np.save('tmp.npy', arr_slice)

方案2:直接通过内存映射修改并截断文件

如果目标是修改原文件内容并缩短长度,可以直接操作内存映射,之后手动截断文件:

import numpy as np
import os

np.save('tmp.npy', np.empty(128))
tmp = np.load('tmp.npy', mmap_mode='r+')
# 对切片内容进行修改(示例为赋值0)
tmp[:64] = np.zeros(64)
# 将修改刷回磁盘
tmp.flush()

# 手动截断文件到目标大小:npy文件头 + 64个元素的字节数
with open('tmp.npy', 'rb') as f:
    # 读取npy文件头标识与长度
    magic = f.read(6)
    if magic == b'\x93NUMPY':
        f.read(2)  # 跳过版本号
        header_len = np.frombuffer(f.read(2), dtype=np.uint16)[0]
        total_header_len = 6 + 2 + 2 + header_len
# 计算新文件大小(假设元素为默认float64,每个占8字节)
new_size = total_header_len + 64 * 8
os.truncate('tmp.npy', new_size)

内容的提问来源于stack exchange,提问作者Muhammad Ikhwan Perwira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 06:22:37