
当面对海量文件目录时,os.listdir() 可能因一次性加载全部文件名导致内存激增甚至 ide 崩溃;本文介绍使用 os.scandir() 配合惰性过滤与随机抽样,在不遍历全量文件名的前提下,高效、低开销地随机选取 k 个文件(如 64 张 PNG 图像),特别适用于构建轻量 DataLoader。
当面对海量文件目录时,`os.listdir()` 可能因一次性加载全部文件名导致内存激增甚至 ide 崩溃;本文介绍使用 `os.scandir()` 配合惰性过滤与随机抽样,在不遍历全量文件名的前提下,高效、低开销地随机选取 k 个文件(如 64 张 png 图像),特别适用于构建轻量 dataloader。
在深度学习数据加载场景中(例如 pytorch 训练循环),我们常需从大型图像目录中随机采样一个 mini-batch(如 64 张 .png 文件)。若直接调用 os.listdir(path),Python 会将目录下所有文件名一次性读入内存——当目录含数万甚至百万级文件时,这不仅拖慢响应,更可能触发 VS Code 等编辑器因内存超限而崩溃。
os.scandir() 是 Python 3.5+ 引入的替代方案,它返回一个可迭代的 DirEntry 对象流,支持延迟获取文件属性(如是否为文件、扩展名等),显著降低内存占用和初始化开销。结合列表推导式进行轻量过滤(仅保留文件)与 random.sample() 的无放回随机抽样,即可实现「按需加载 + 精准采样」的高效组合:
import os import random output_dir = 'data/images' num_images = 64 # 使用 scandir 迭代获取 DirEntry,仅保留普通文件(跳过子目录/符号链接) entries = os.scandir(output_dir) filenames = [entry.name for entry in entries if entry.is_file() and entry.name.lower().endswith('.png')] # 安全检查:确保样本数不超过可用文件数 if len(filenames) < num_images: raise ValueError(f"目录中仅有 {len(filenames)} 个文件,不足所需 {num_images} 个") # 随机无放回抽样 k 个索引 indices = random.sample(range(len(filenames)), k=num_images) selected_files = [os.path.join(output_dir, filenames[i]) for i in indices] # 后续可直接用于图像加载(例如用 PIL 或 OpenCV) # for img_path in selected_files: # img = Image.open(img_path).convert('RGB')
⚠️ 关键注意事项:
- os.scandir() 返回的对象不可重复迭代,因此需立即转为列表(如 filenames)以支持多次索引访问;
- 示例中增加了 .png 后缀校验(大小写不敏感),确保只采样目标图像格式,避免误选日志、隐藏文件等;
- 若目录规模极大(如 >100 万文件)且仅需极小比例样本(如 k=64),可进一步优化为「蓄水池抽样(Reservoir Sampling)」,实现 O(n) 时间 + O(k) 空间复杂度,完全避免存储全部文件名——但对万级以内目录,上述方案已足够简洁高效;
- 实际集成到 torch.utils.data.DataLoader 时,建议封装为自定义 Dataset,在 __getitem__ 中按需解析路径并加载图像,而非预加载全部路径。
该方法兼顾性能、可读性与工程鲁棒性,是处理大规模静态文件集时值得优先采用的标准实践。