推荐用 os.walk 实现树形输出,它天然深度优先、自动处理子目录进出;需注意路径拼接用 os.path.join、捕获 permissionerror、用 abspath 防符号链接循环。

直接用 os.walk 或递归遍历 os.listdir 都能实现,但要注意路径拼接错误、权限拒绝导致中断、符号链接循环这三类问题——它们比缩进逻辑更常让你的树形输出崩掉。
用 os.walk 实现带缩进的树形输出(推荐初学者)
它天然按深度优先遍历,且自动处理子目录进入/退出,不用手动递归控制。关键在利用 topdown=True(默认)和对 dirs 列表的原地修改来控制遍历范围。
- 缩进用
"│ " * depth比字符串重复更直观,depth从root的层级开始算(可设为 0) - 必须用
os.path.join(root, name)拼路径,别用root + "/" + name—— Windows 下会出错 - 遇到
PermissionError要捕获,否则整个遍历会中断;可用onerror=lambda e: None忽略 - 示例片段:
import os def print_tree(root, depth=0): print("│ " * depth + "├─" + os.path.basename(root)) try: for name in sorted(os.listdir(root)): path = os.path.join(root, name) if os.path.isdir(path): print_tree(path, depth + 1) else: print("│ " * (depth + 1) + "├─" + name) except PermissionError: print("│ " * (depth + 1) + "├─[no access]")
手写递归函数时如何避免无限循环(尤其含符号链接)
当目录里有软链接指向父目录或自身时,纯递归会栈溢出。必须记录已访问路径的绝对路径(os.path.abspath),而非相对路径或字符串名。
- 用
set缓存已进入过的abspath,每次进入前检查是否已在其中 - 调用
os.path.islink(path)判断是否为链接,再决定是否跳过或特殊标记(如加@后缀) - 不要依赖
os.path.realpath解析后比较——它可能绕过你本想保留的链接语义 - 错误示范:
visited.add(name)→ 同名不同路径就失效;正确做法:visited.add(os.path.abspath(path))
pathlib.Path 版本更简洁,但要注意 iterdir() 不递归
pathlib 写法干净,但它的 iterdir() 只返回当前层,递归逻辑仍需自己写。优势在于路径操作安全(自动处理分隔符)、支持模式匹配(glob("*.py"))。
- 用
p.resolve()获取绝对路径并去重,比os.path.abspath更可靠 -
Path.is_symlink()和Path.is_dir()返回布尔值,无需再包一层os.path - 缩进建议用
str(p.relative_to(root)).count(os.sep)算深度,比手动传参更少出错 - 注意:Windows 下
\?前缀路径可能导致relative_to报错,需提前规整
真正卡住人的往往不是“怎么缩进”,而是某个子目录打不开、某个链接转晕了、或者路径拼错后报出一串你看不懂的 FileNotFoundError: [WinError 2]。先确保每一步的 os.path.exists 或 Path.exists() 都校验过,再谈美观。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











