你还在这样 “低效遍历” 目录文件吗?
处理文件时,你是不是遇到过这些问题:
用os.listdir()遍历目录,还要手动判断 “是文件还是文件夹”,代码写得又长又乱;
遍历多层目录时,写递归写得头晕,还容易出现 “递归深度超限”;
遍历大目录(上千个文件)时,程序卡半天,CPU 占用率飙升……
其实 Python 里藏着 “高效遍历目录” 的绝招,不用复杂逻辑,几行代码就能搞定 “单目录遍历”“多层目录遍历”“按条件筛选文件”,今天就把这招教给你,从基础用法到进阶优化,看完就能用。

核心绝招:用 os.scandir () 实现 “高效遍历”
很多人知道os.listdir(),却少有人用os.scandir()—— 这是 Python 3.5 + 推出的 “高效目录遍历函数”,比os.listdir()快 2-3 倍,原因很简单:它会直接获取文件的 “类型、大小、修改时间” 等元信息,不用像os.listdir()那样再调用os.path.isfile()/os.path.isdir()二次查询。
先看最常用的场景:遍历指定目录,分别获取 “文件列表” 和 “文件夹列表”,用os.scandir()只需 5 行代码:
import os# 目标目录路径(绝对路径/相对路径都可以)target_dir = "./my_files" # 相对路径:当前目录下的my_files文件夹# target_dir = "/home/user/documents" # 绝对路径(Linux)# 遍历目录with os.scandir(target_dir) as entries:# 筛选文件(is_file()直接判断,无需额外调用os.path)files = [entry.name for entry in entries if entry.is_file()]# 重新定位到目录开头(因为entries是迭代器,遍历一次后会耗尽)entries.seek(0)# 筛选文件夹folders = [entry.name for entry in entries if entry.is_dir()]# 打印结果print("目录下的文件:", files)print("目录下的文件夹:", folders)
关键优势:
用entry.is_file()/entry.is_dir()直接判断,比os.path.isfile(os.path.join(dir, name))少了 “拼接路径 + 二次查询”,效率更高;
用with语句包裹,会自动关闭资源,避免内存泄漏;
entry.name获取文件名,entry.path获取完整路径,按需选用,不用手动拼接。
如果要遍历 “目录 + 所有子目录”,不用写递归,搭配os.walk()?不,其实os.scandir()也能实现,而且更灵活。不过更推荐用os.scandir()结合 “生成器”,既高效又能控制遍历深度:
import osdef scan_all_files(root_dir, max_depth=None):"""遍历多层目录,获取所有文件:param root_dir: 根目录:param max_depth: 最大遍历深度(None表示无限制):return: 生成器,逐个返回文件的完整路径"""# 用os.scandir()遍历当前目录with os.scandir(root_dir) as entries:for entry in entries:# 计算当前深度(根目录为1,子目录为2,以此类推)current_depth = root_dir.count(os.sep) + 1# 如果是文件,直接返回路径if entry.is_file():yield entry.path# 如果是文件夹,且没超过最大深度,递归遍历子目录elif entry.is_dir() and (max_depth is None or current_depth < max_depth):# 递归调用,传入子目录路径yield from scan_all_files(entry.path, max_depth)# 用法示例:遍历./my_files目录及其子目录,最大深度3层all_files = list(scan_all_files("./my_files", max_depth=3))print("所有文件(含子目录):", all_files)
核心亮点:
用 “生成器(yield)” 返回结果,遍历大目录时不会一次性加载所有文件到内存,内存占用率极低;
支持max_depth控制深度,比如只遍历 “根目录 + 1 层子目录”,避免无限制遍历;
代码逻辑清晰,比os.walk()更易修改(比如想在遍历中跳过某个文件夹,加个判断即可)。
3. 高级用法:按条件筛选文件(按后缀、按修改时间)
实际场景中,我们常需要 “筛选特定文件”—— 比如只要.txt和.md文件、只保留 “3 天内修改过的文件”,用os.scandir()搭配条件判断,轻松实现:
import osfrom datetime import datetime, timedeltadef filter_files(root_dir, suffix_list=None, days_ago=None):"""按条件筛选文件:按后缀、按修改时间:param root_dir: 根目录:param suffix_list: 要保留的文件后缀(如[".txt", ".md"]):param days_ago: 保留“n天内修改过”的文件(如3表示保留3天内的):return: 筛选后的文件路径列表"""# 默认值:不按后缀筛选,不按时间筛选suffix_list = suffix_list or []# 计算“n天前”的时间戳(用于判断修改时间)cutoff_time = datetime.now() - timedelta(days=days_ago) if days_ago else Nonecutoff_timestamp = cutoff_time.timestamp() if cutoff_time else Nonefiltered = []with os.scandir(root_dir) as entries:for entry in entries:if entry.is_file():# 条件1:按后缀筛选(不区分大小写)if suffix_list:# 获取文件后缀(如".txt")file_suffix = os.path.splitext(entry.name)[1].lower()if file_suffix not in suffix_list:continue # 不满足后缀条件,跳过# 条件2:按修改时间筛选if cutoff_timestamp:# entry.stat()获取文件元信息,st_mtime是修改时间戳modify_time = entry.stat().st_mtimeif modify_time < cutoff_timestamp:continue # 修改时间太早,跳过# 满足所有条件,加入列表filtered.append(entry.path)return filtered# 用法示例:筛选./my_files目录下,后缀为.txt/.md,且3天内修改过的文件result = filter_files(root_dir="./my_files",suffix_list=[".txt", ".md"],days_ago=3)print("筛选后的文件:", result)
关键技巧:
用os.path.splitext(entry.name)[1]获取文件后缀,搭配lower()实现 “不区分大小写”(比如.TXT也能被识别);
用entry.stat().st_mtime获取 “修改时间戳”,比os.path.getmtime()更快,因为os.scandir()已提前缓存元信息;
多条件筛选时,用 “continue” 跳过不满足的文件,代码逻辑更清晰,比嵌套if更易维护。
补充方案:用 pathlib 实现 “面向对象” 的遍历
如果喜欢 “面向对象” 风格的代码,Python 3.4 + 的pathlib模块是另一个好选择 —— 它把 “路径” 封装成对象,调用方法更直观,和os.scandir()效率差不多,适合喜欢简洁语法的同学。
from pathlib import Path# 目标目录(Path对象)target_dir = Path("./my_files")# 筛选文件:用.iterdir()遍历,is_file()判断files = [file.name for file in target_dir.iterdir() if file.is_file()]# 筛选文件夹folders = [folder.name for folder in target_dir.iterdir() if folder.is_dir()]print("文件列表:", files)print("文件夹列表:", folders)
想遍历所有子目录,且按后缀匹配(比如所有.py文件),pathlib的.rglob()更方便,一行代码搞定:
from pathlib import Path# 遍历./my_files及其子目录,获取所有.py文件(rglob=recursive glob)py_files = list(Path("./my_files").rglob("*.py"))# 转换为字符串路径(方便后续处理)py_files_str = [str(file) for file in py_files]print("所有Python文件:", py_files_str)
优势对比:
os.scandir():更侧重 “高效获取元信息”,适合需要频繁判断文件类型、获取修改时间的场景;
pathlib:更侧重 “简洁语法”,适合按后缀匹配、路径拼接等场景,代码更易读。
两者都比os.listdir()高效,根据自己的编码习惯选即可。

避坑与优化:让遍历更稳定、更快
遍历目录时,遇到 “权限不足” 的文件夹会报错PermissionError,用try-except捕获即可:
import osdef safe_scan_dir(root_dir):with os.scandir(root_dir) as entries:for entry in entries:try:if entry.is_file():yield entry.pathelif entry.is_dir():yield from safe_scan_dir(entry.path)except PermissionError:print(f"权限不足,跳过目录:{entry.path}")continue# 用法all_files = list(safe_scan_dir("./my_files"))
遍历上千个文件的目录时,不要用list()一次性把所有文件存到内存,用生成器逐个处理,内存占用会从 “几十 MB” 降到 “几 KB”:
# 低效:一次性加载所有文件到列表all_files = list(os.scandir("./big_dir")) # 大目录会占用大量内存# 高效:用生成器逐个处理def scan_big_dir(root_dir):with os.scandir(root_dir) as entries:for entry in entries:if entry.is_file():yield entry.path # 逐个返回,不占内存# 用法:循环处理每个文件for file_path in scan_big_dir("./big_dir"):# 处理文件(如读取内容、复制文件等)with open(file_path, "r") as f:content = f.read()print(f"处理文件:{file_path},内容长度:{len(content)}")
遍历后想按 “文件修改时间” 排序(最新修改的在前),用entry.stat().st_mtime作为排序键:
import ostarget_dir = "./my_files"with os.scandir(target_dir) as entries:# 筛选文件并按修改时间排序(reverse=True表示倒序,最新的在前)sorted_files = sorted([entry for entry in entries if entry.is_file()],key=lambda x: x.stat().st_mtime,reverse=True)# 打印结果for file in sorted_files:# 转换时间戳为可读格式modify_time = datetime.fromtimestamp(file.stat().st_mtime).strftime("%Y-%m-%d %H:%M:%S")print(f"文件:{file.name},修改时间:{modify_time}")
总结:1 分钟掌握核心用法

Python 遍历目录文件,不用记复杂的递归逻辑,掌握os.scandir()或pathlib,根据场景选对方法,几行代码就能高效搞定。下次处理文件时,别再用os.listdir()写冗长的判断了,试试今天教的这招,效率会明显提升!
这款收获无数好评的终生学习小程序:码途钥匙
2025 全方位 Python 工程师与 AI 算法专业班,课程规划细致又全面!从 Python 基础筑牢根基,到全栈开发、高级框架的深度钻研,再到 AI 算法与数据分析的前沿探秘,每个阶段都干货满满。无论是想入门 Python,还是进阶从事 AI 相关工作,在这里都能找到适配的内容。而且还有配套的课程资源、公开课以及海量题库,助力大家在 IT 学习之路上稳步迈进~




