前言
- 傳統上我們用 os、os.path 來處理路徑;現代 Python 推薦使用 pathlib.Path。
- Path 是物件導向、跨平台且可讀性高。以下以「對照+範例」方式帶你快速上手,所有程式碼皆為可複製的程式碼區塊,適合在 Jupyter 逐格執行。
一、建立與檢視路徑(os vs Path)
# os.path 寫法
import os
p_os = os.path.join("data", "images", "cat.jpg")
print(p_os, type(p_os))
# pathlib.Path 寫法:用 / 直覺拼接(跨平台自動處理分隔符)
from pathlib import Path
p = Path("data") / "images" / "cat.jpg"
print(p, str(p), p.as_posix(), type(p))
輸出:
p, str(p), p.as_posix(), type(p)
路徑分隔符:
二、拆解與操作路徑
# %%
# os.path 寫法
import os
p_str = "data/images/cat.jpg"
dirname = os.path.dirname(p_str)
basename = os.path.basename(p_str)
stem, ext = os.path.splitext(basename)
print("dir:", dirname, "name:", basename, "stem:", stem, "ext:", ext)
# %%
# Path 寫法:屬性一目了然
from pathlib import Path
p = Path("data/images/cat.jpg")
print("parent:", p.parent)
print("name:", p.name)
print("stem:", p.stem)
print("suffix:", p.suffix)
print("parents:", list(p.parents)[:3]) # 多層父目錄
print("換副檔名:", p.with_suffix(".png"))
print("加子路徑:", p.parent / "thumbs" / p.name)
輸出:
三、存在性與建立資料夾
# os 寫法
import os
print(os.path.exists("data"), os.path.isfile("data"), os.path.isdir("data"))
if not os.path.exists("data/output"):
os.makedirs("data/output", exist_ok=True)
# Path 寫法
from pathlib import Path
d = Path("data")
print(d.exists(), d.is_file(), d.is_dir())
(d / "output").mkdir(parents=True, exist_ok=True)
輸出:
四、讀寫文字檔
# os + open
text_path = "data/output/notes.txt"
with open(text_path, "w", encoding="utf-8") as f:
f.write("Hello\n")
with open(text_path, "a", encoding="utf-8") as f:
f.write("World\n")
with open(text_path, "r", encoding="utf-8") as f:
print(f.read())
# Path 快捷:read_text / write_text / open
from pathlib import Path
text_path = Path("data/output/notes.txt")
text_path.parent.mkdir(parents=True, exist_ok=True)
text_path.write_text("Hello\nWorld\n", encoding="utf-8")
print(text_path.read_text(encoding="utf-8"))
輸出:
五、走訪與過濾檔案(glob/rglob)
# os.walk
import os
all_py = []
for root, dirs, files in os.walk(r"D:\Temp"):
for f in files:
if f.endswith(".py"):
all_py.append(os.path.join(root, f))
print(all_py[:5])
# Path 的 glob(非遞迴)與 rglob(遞迴)
from pathlib import Path
print(list(Path(r"D:\Temp").glob("*.txt"))[:5]) # 當層 *.txt
print(list(Path(r"D:\Temp").rglob("*.py"))[:5]) # 遞迴 *.py
輸出:
六、複製、移動、刪除(搭配 shutil)
# os + shutil
import os, shutil
src = "data/input/a.txt"
dst = "data/output/a.txt"
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst) # 複製並保留 metadata
os.remove(dst) # 刪除
# Path + shutil
from pathlib import Path
import shutil
src = Path("data/input/a.txt")
dst = Path("data/output/a.txt")
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
dst.unlink() # 刪除檔案(等同 os.remove)
# 刪資料夾:Path("some_dir").rmdir() # 空資料夾
# 刪非空資料夾:shutil.rmtree(Path("some_dir"))
七、檔案資訊與美化時間
# os.stat
import os, time
st = os.stat("data/images/cat.jpg")
print("size:", st.st_size, "mtime:", time.ctime(st.st_mtime))
# Path.stat + datetime
from pathlib import Path
from datetime import datetime
p = Path("data/images/cat.jpg")
st = p.stat()
print("size:", st.st_size)
print("mtime:", datetime.fromtimestamp(st.st_mtime).strftime("%Y-%m-%d %H:%M:%S"))
八、目前工作目錄與絕對路徑
# os
import os
print(os.getcwd())
print(os.path.abspath("."))
# Path
from pathlib import Path
print(Path.cwd())
print(Path(".").resolve())
九、切換目錄(臨時,確保切回來)
import os
from pathlib import Path
cwd = Path.cwd()
try:
os.chdir("data")
print("now in:", Path.cwd())
finally:
os.chdir(cwd)
print("back to:", Path.cwd())
十、展開家目錄與環境變數
# os
import os
print(os.path.expanduser("~/projects"))
print(os.path.expandvars("%USERPROFILE%/projects" if os.name == "nt" else "$HOME/projects"))
# Path + os
from pathlib import Path
import os
print(Path("~/projects").expanduser())
print(Path(os.path.expandvars("$HOME/projects")))
十一、相對路徑與比較
# %%
# os.path.relpath:兩者不需父子關係也可計算
import os
print(os.path.relpath("/a/b/c", "/a")) # 'b/c'
# %%
# Path.relative_to:要求左邊包含右邊,否則會丟例外
from pathlib import Path
print(Path("/a/b/c").relative_to("/a")) # Path('b/c')
輸出:
十二、小專案實作:合併資料夾內所有 .txt 到一個檔案
from pathlib import Path
src_dir = Path("data/texts")
out = Path("data/merged.txt")
out.parent.mkdir(parents=True, exist_ok=True)
with out.open("w", encoding="utf-8") as fout:
for txt in sorted(src_dir.rglob("*.txt")):
fout.write(f"--- {txt.name} ---\n")
fout.write(txt.read_text(encoding="utf-8"))
fout.write("\n\n")
print("Written:", out.resolve())
十三、Path 小抄(最常用的幾行)
from pathlib import Path
# 建立路徑
p = Path("base") / "sub" / "file.txt"
# 路徑組件
print(p.parent, p.name, p.stem, p.suffix)
# 建資料夾
(Path("logs") / "today").mkdir(parents=True, exist_ok=True)
# 讀寫檔案
Path("note.txt").write_text("hello", encoding="utf-8")
print(Path("note.txt").read_text(encoding="utf-8"))
# 走訪
files = list(Path("base").rglob("*.py"))
print(files[:5])
結語
- 新專案優先使用 pathlib.Path;它讓程式更直覺、跨平台更安心。
- 仍可與 os、shutil 等共用:需要字串時用 str(Path物件),需要系統呼叫時用 os。
- 先記住三件事就能快速上手:用 / 拼路徑、用 read_text/write_text 存取、用 glob/rglob 走訪。
推薦hahow線上學習python: https://igrape.net/30afN