目錄遍歷是檔案處理、批次作業、資料收集的基礎操作。Python 提供多種方式:os.listdir(單層)、os.walk(遞迴、最常用)、glob(模式匹配)、pathlib(現代物件導向)。本文系統比較各方法特性、參數、效能與實戰範例。
| 方法 | 模組 | 遞迴 | 回傳型別 | 適用場景 |
|---|---|---|---|---|
| os.listdir | os | 否(單層) | list[str] | 只需當前目錄檔案列表 |
| os.walk | os | 是(預設深度優先) | generator of (root, dirs, files) | 完整目錄樹遍歷、批次處理 |
| glob.glob | glob | 支援 ** 遞迴 | list[str] | 模式匹配檔案(如 *.py) |
| pathlib.Path.rglob | pathlib | 是 | generator[Path] | 現代 OO 風格、路徑操作豐富 |
| os.scandir | os | 否 | iterator[DirEntry] | 高效能單層掃描(Python 3.5+) |
import os
for root, dirs, files in os.walk(top, topdown=True, onerror=None, followlinks=False):
# root: 當前目錄路徑 (str)
# dirs: 子目錄名稱列表 (list[str])
# files: 檔案名稱列表 (list[str])
pass
True(預設)= 先處理父目錄再遞迴子目錄;False = 後序遍歷(先處理最深層)True 追蹤符號連結(預設 False,避免無限迴圈)
import os
for root, dirs, files in os.walk('/path/to/dir'):
for file in files:
full_path = os.path.join(root, file)
print(full_path)
# topdown=True (預設):預序遍歷
# 輸出順序:/a → /a/b → /a/b/c → /a/d
# topdown=False:後序遍歷
# 輸出順序:/a/b/c → /a/b → /a/d → /a
# 適用:刪除目錄樹(必須先刪子目錄再刪父目錄)
import os
for root, dirs, files in os.walk('/path/to/dir', topdown=True):
# 過濾不想進入的目錄(如 .git, __pycache__, venv)
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ('__pycache__', 'venv')]
for file in files:
if file.endswith('.py'):
print(os.path.join(root, file))
關鍵:必須用 dirs[:] = ... 就地修改列表內容,重新指派 dirs = ... 無效。
import os
def list_all_files(start_path, result=None):
if result is None:
result = []
for name in os.listdir(start_path):
full = os.path.join(start_path, name)
if os.path.isdir(full):
list_all_files(full, result)
else:
result.append(full)
return result
files = list_all_files('/path/to/dir')
from pathlib import Path
root = Path('/path/to/dir')
# 遞迴遍歷所有檔案
for file_path in root.rglob('*'):
if file_path.is_file():
print(file_path) # Path 物件
print(file_path.name) # 檔名
print(file_path.suffix) # 副檔名
print(file_path.stat().st_size) # 檔案大小
# 模式匹配
for py_file in root.rglob('*.py'):
print(py_file)
# 非遞迴:iterdir()
for item in root.iterdir():
print(item.name, 'DIR' if item.is_dir() else 'FILE')
import glob
# 非遞迴
glob.glob('/path/to/dir/*.py')
# 遞迴 (Python 3.5+)
glob.glob('/path/to/dir/**/*.py', recursive=True)
# 含路徑分隔符的模式
glob.glob('/path/to/dir/*/*.txt')
import os
with os.scandir('/path/to/dir') as entries:
for entry in entries:
# entry 是 DirEntry 物件,擁有快取的 stat 資訊
print(entry.name, entry.is_dir(), entry.is_file(), entry.stat().st_size)
優勢:避免重複 os.path.join + os.stat 系統呼叫,大目錄下顯著加速。
import os
total_files = 0
total_size = 0
for root, dirs, files in os.walk('/path'):
for f in files:
fp = os.path.join(root, f)
total_files += 1
total_size += os.path.getsize(fp)
print(f"Files: {total_files}, Size: {total_size/1024/1024:.2f} MB")
import os
large_files = []
for root, dirs, files in os.walk('/path'):
for f in files:
fp = os.path.join(root, f)
size = os.path.getsize(fp)
if size > 100 * 1024 * 1024:
large_files.append((fp, size))
for fp, size in sorted(large_files, key=lambda x: -x[1]):
print(f"{size/1024/1024:.1f} MB {fp}")
import os
for root, dirs, files in os.walk('/path'):
for f in files:
if f.endswith('.jpeg'):
old = os.path.join(root, f)
new = os.path.join(root, f[:-5] + '.jpg')
os.rename(old, new)
print(f"Renamed: {old} -> {new}")
import os
src = '/source'
dst = '/target'
for root, dirs, files in os.walk(src):
rel = os.path.relpath(root, src)
target_dir = os.path.join(dst, rel) if rel != '.' else dst
os.makedirs(target_dir, exist_ok=True)
| 場景 | 推薦方法 | 理由 |
|---|---|---|
| 完整目錄樹遍歷、批次處理 | os.walk | 標準庫、生成器記憶體效率、支援 topdown 控制 |
| 現代專案、路徑操作多 | pathlib.Path.rglob | OO 設計、Path 物件功能豐富、程式碼可讀性高 |
| 單純模式匹配搜尋 | glob.glob | 語法簡潔、支援 ** 遞迴 |
| 超大目錄、效能敏感 | os.scandir + 手動遞迴 | 避免重複 stat 系統呼叫、最快 |
| 單層列表 | os.listdir / os.scandir | 簡單直接 |
# 陷阱 1:os.walk 預設不跟隨符號連結
# 解決:followlinks=True(小心無限迴圈)
# 陷阱 2:修改 dirs 列表時用 dirs = [...] 而非 dirs[:] = [...]
# 錯誤:
dirs = [d for d in dirs if ...] # 無效!
# 正確:
dirs[:] = [d for d in dirs if ...] # 就地修改,影響遞迴
# 陷阱 3:路徑拼接用字串 + 而非 os.path.join
# 錯誤:root + '/' + file (Windows 失敗)
# 正確:os.path.join(root, file) 或 Path(root) / file
# 陷阱 4:遍歷時刪除檔案/目錄導致錯誤
# 解決:先收集路徑列表,遍歷結束後再刪除
Python 目錄遍歷首選 os.walk(標準、高效、可控制遞迴),現代專案推薦 pathlib.Path.rglob(物件導向、功能豐富)。掌握 topdown 控制順序、dirs[:] 過濾目錄、os.path.join 跨平台路徑、os.scandir 高效能掃描等核心技巧,能高效完成檔案搜尋、批次處理、統計分析等實務任務。