快轉到主要內容
  1. 教學文章/

pathlib:優雅處理檔案路徑的現代方式

·6 分鐘· loading · loading · ·
Python Pathlib 檔案處理 標準庫
每日拍拍
作者
每日拍拍
科學家 X 科技宅宅
目錄
Python 學習 - 本文屬於一個選集。
§ 16: 本文

一. 前言
#

嗨,大家好!我是拍拍君 🎉

你有沒有寫過這樣的程式碼?

import os

base_dir = "/home/pypy君/projects"
data_dir = os.path.join(base_dir, "data")
file_path = os.path.join(data_dir, "report.csv")

if os.path.exists(file_path):
    with open(file_path, "r") as f:
        content = f.read()
    filename = os.path.basename(file_path)
    extension = os.path.splitext(file_path)[1]
    parent = os.path.dirname(file_path)

一堆 os.path.joinos.path.existsos.path.basename⋯⋯光看就覺得累 😵

Python 3.4 開始,標準庫就內建了一個超優雅的模組:pathlib。它把檔案路徑變成物件,讓你可以用直覺的方式操作路徑,程式碼瞬間變得乾淨又好讀!

上面那段程式碼用 pathlib 改寫:

from pathlib import Path

base_dir = Path("/home/pypy君/projects")
file_path = base_dir / "data" / "report.csv"

if file_path.exists():
    content = file_path.read_text()
    filename = file_path.name
    extension = file_path.suffix
    parent = file_path.parent

是不是清爽很多?用 / 運算子串接路徑,用屬性取得檔名,用方法讀寫檔案——一切都那麼自然。

今天拍拍君就帶大家徹底學會 pathlib


二. 安裝
#

好消息:不用安裝! pathlib 是 Python 3.4+ 的標準庫模組,直接 import 就能用:

from pathlib import Path

如果你還在用 Python 2⋯⋯拜託趕快升級吧 😂


三. 建立路徑物件
#

基本建立
#

from pathlib import Path

# 從字串建立
p = Path("/Users/pypy君/Documents")

# 當前目錄
cwd = Path.cwd()
print(cwd)  # /Users/pypy君/projects

# 家目錄
home = Path.home()
print(home)  # /Users/pypy君

/ 運算子串接
#

這是 pathlib 最經典的用法——用除法符號串接路徑:

project = Path.home() / "code" / "my_project"
config = project / "config" / "settings.yaml"

print(project)  # /Users/pypy君/code/my_project
print(config)   # /Users/pypy君/code/my_project/config/settings.yaml

比起 os.path.join(os.path.join(base, "config"), "settings.yaml") 簡潔太多了!

跨平台自動處理
#

pathlib 會根據你的作業系統自動使用正確的路徑分隔符:

# 在 macOS/Linux 上
p = Path("data") / "output" / "result.csv"
print(p)  # data/output/result.csv

# 在 Windows 上(同樣的程式碼)
# data\output\result.csv

你再也不用煩惱 / 還是 \ 的問題了!


四. 路徑屬性:拆解路徑的各個部分
#

Path 物件提供了超多好用的屬性,讓你輕鬆取得路徑的各個組成部分:

p = Path("/Users/pypy君/projects/data/report_2026.csv")

print(p.name)       # report_2026.csv    (完整檔名)
print(p.stem)       # report_2026        (不含副檔名的檔名)
print(p.suffix)     # .csv               (副檔名)
print(p.suffixes)   # ['.csv']           (所有副檔名)
print(p.parent)     # /Users/pypy君/projects/data  (上層目錄)
print(p.anchor)     # /                  (根目錄)
print(p.parts)      # ('/', 'Users', 'pypy君', 'projects', 'data', 'report_2026.csv')

多重副檔名
#

有些檔案有多個副檔名(像 .tar.gz),pathlib 也能處理:

archive = Path("backup.tar.gz")

print(archive.suffix)    # .gz
print(archive.suffixes)  # ['.tar', '.gz']
print(archive.stem)      # backup.tar

修改路徑的部分
#

p = Path("/data/old_report.csv")

# 改檔名
new_p = p.with_name("new_report.csv")
print(new_p)  # /data/new_report.csv

# 改副檔名
json_p = p.with_suffix(".json")
print(json_p)  # /data/old_report.json

# 改 stem(Python 3.9+)
renamed = p.with_stem("final_report")
print(renamed)  # /data/final_report.csv

五. 檔案操作:讀寫就是這麼簡單
#

讀取檔案
#

不用再寫 with open(...) as f 了(當然,大檔案還是建議用):

p = Path("config.yaml")

# 讀取文字
text = p.read_text(encoding="utf-8")

# 讀取二進位
data = p.read_bytes()

寫入檔案
#

output = Path("result.txt")

# 寫入文字
output.write_text("拍拍君到此一遊!\n", encoding="utf-8")

# 寫入二進位
output.write_bytes(b"\x89PNG...")

⚠️ 注意:write_text()write_bytes()覆蓋原有內容!如果要追加,還是得用 open(p, "a") 的方式。

建立與刪除
#

# 建立目錄
data_dir = Path("output") / "2026" / "02"
data_dir.mkdir(parents=True, exist_ok=True)
# parents=True → 自動建立中間目錄
# exist_ok=True → 目錄已存在也不會報錯

# 建立空檔案(類似 touch)
new_file = data_dir / "placeholder.txt"
new_file.touch()

# 刪除檔案
new_file.unlink()

# 刪除空目錄
data_dir.rmdir()

💡 如果要刪除非空目錄,需要搭配 shutil.rmtree()pathlib 本身只能刪空目錄。


六. 遍歷與搜尋:找到你要的檔案
#

列出目錄內容
#

project = Path("my_project")

# 列出直接子項目
for item in project.iterdir():
    print(item)

# 只列出檔案
files = [f for f in project.iterdir() if f.is_file()]

# 只列出目錄
dirs = [d for d in project.iterdir() if d.is_dir()]

glob 模式搜尋
#

glob()pathlib 的殺手級功能之一:

project = Path("my_project")

# 找所有 Python 檔案
py_files = list(project.glob("*.py"))

# 找所有子目錄中的 Python 檔案
all_py = list(project.glob("**/*.py"))

# 找所有圖片
images = list(project.glob("**/*.png")) + list(project.glob("**/*.jpg"))

# 找特定命名模式
logs = list(project.glob("log_202?.txt"))  # log_2024.txt, log_2025.txt...

rglob:遞迴搜尋的捷徑
#

rglob() 等同於 glob("**/pattern"),更簡潔:

# 這兩行是等價的
project.glob("**/*.py")
project.rglob("*.py")

# 找所有 Markdown 檔案
docs = list(Path("notes").rglob("*.md"))

七. 路徑判斷:這是什麼?
#

p = Path("some_path")

p.exists()       # 路徑是否存在
p.is_file()      # 是否為檔案
p.is_dir()       # 是否為目錄
p.is_symlink()   # 是否為符號連結
p.is_absolute()  # 是否為絕對路徑

實用範例:安全地處理檔案
#

def safe_read(path: str | Path) -> str | None:
    """安全讀取檔案,不存在就回傳 None"""
    p = Path(path)
    if not p.exists():
        print(f"⚠️ 檔案不存在:{p}")
        return None
    if not p.is_file():
        print(f"⚠️ 不是檔案:{p}")
        return None
    return p.read_text(encoding="utf-8")

八. 實戰:用 pathlib 整理專案檔案
#

來看一個完整的實戰例子——整理下載資料夾裡的檔案:

from pathlib import Path
from datetime import datetime

def organize_downloads(download_dir: str = "~/Downloads"):
    """依照副檔名分類檔案到子資料夾"""
    downloads = Path(download_dir).expanduser()

    # 分類規則
    categories = {
        "圖片": {".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"},
        "文件": {".pdf", ".doc", ".docx", ".txt", ".md"},
        "程式碼": {".py", ".js", ".ts", ".html", ".css", ".json"},
        "壓縮檔": {".zip", ".tar", ".gz", ".7z", ".rar"},
        "影片": {".mp4", ".mov", ".avi", ".mkv"},
    }

    moved = 0
    for file in downloads.iterdir():
        if not file.is_file() or file.name.startswith("."):
            continue

        # 找出分類
        category = "其他"
        for cat, extensions in categories.items():
            if file.suffix.lower() in extensions:
                category = cat
                break

        # 移動到對應資料夾
        target_dir = downloads / category
        target_dir.mkdir(exist_ok=True)

        target = target_dir / file.name
        if target.exists():
            # 加上時間戳避免覆蓋
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            target = target_dir / f"{file.stem}_{timestamp}{file.suffix}"

        file.rename(target)
        moved += 1
        print(f"📁 {file.name}{category}/")

    print(f"\n✅ 共整理了 {moved} 個檔案!")


if __name__ == "__main__":
    organize_downloads()

執行結果:

📁 report.pdf → 文件/
📁 screenshot.png → 圖片/
📁 data.json → 程式碼/
📁 archive.zip → 壓縮檔/

✅ 共整理了 4 個檔案!

九. os.path vs pathlib 對照表
#

如果你習慣用 os.path,這張對照表可以幫你快速切換:

功能 os.path 寫法 pathlib 寫法
串接路徑 os.path.join(a, b) Path(a) / b
取檔名 os.path.basename(p) p.name
取副檔名 os.path.splitext(p)[1] p.suffix
取目錄 os.path.dirname(p) p.parent
是否存在 os.path.exists(p) p.exists()
是否為檔案 os.path.isfile(p) p.is_file()
是否為目錄 os.path.isdir(p) p.is_dir()
絕對路徑 os.path.abspath(p) p.resolve()
展開 ~ os.path.expanduser(p) p.expanduser()
讀取檔案 open(p).read() p.read_text()
列出目錄 os.listdir(p) p.iterdir()
遞迴搜尋 glob.glob("**/*", recursive=True) p.rglob("*")

看出差別了嗎?pathlib 的寫法幾乎都更短、更直覺!


十. 小技巧與注意事項
#

1. 與字串的轉換
#

有些函式庫還不支援 Path 物件,需要轉回字串:

p = Path("/data/file.csv")

# Path → str
str(p)  # "/data/file.csv"

# str → Path
Path("/data/file.csv")

# 大部分現代函式庫都支援 Path 物件了
import json
data = json.loads(Path("data.json").read_text())

2. 用 Type Hint 標注
#

from pathlib import Path

def process_file(input_path: Path, output_dir: Path) -> Path:
    """處理檔案並回傳輸出路徑"""
    output = output_dir / f"processed_{input_path.name}"
    content = input_path.read_text()
    # ... 處理邏輯 ...
    output.write_text(content)
    return output

3. 搭配 f-string
#

project = Path("my_project")
version = "1.0.0"

release_dir = project / "releases" / version
print(f"發佈目錄:{release_dir}")
# 發佈目錄:my_project/releases/1.0.0

結語
#

pathlib 是拍拍君最推薦的 Python 標準庫模組之一。它讓原本冗長又容易出錯的檔案路徑操作,變得優雅又安全。

重點回顧:

  • 🛤️ 用 Path 物件取代字串路徑
  • ➗ 用 / 運算子串接路徑,告別 os.path.join
  • 📖 用 .read_text() / .write_text() 快速讀寫
  • 🔍 用 .glob() / .rglob() 搜尋檔案
  • 🏷️ 用 .name.stem.suffix 取得路徑資訊

從今天開始,試著在新專案裡全面使用 pathlib 吧!一旦習慣了,你就回不去 os.path 了 😆

我們下次見!👋


延伸閱讀
#

Python 學習 - 本文屬於一個選集。
§ 16: 本文

相關文章

httpx:Python 新世代 HTTP 客戶端完全攻略
·4 分鐘· loading · loading
Python Httpx HTTP Async Requests
超快速 Python 套件管理:uv 完全教學
·6 分鐘· loading · loading
Python Uv Package Manager Rust
Python 資料驗證小幫手:Pydantic
·4 分鐘· loading · loading
Python Pydantic Data Validation
科學計算:數值積分
·5 分鐘· loading · loading
Python Numpy Scipy Numerical Methods Numerical Integral
讓你的終端機華麗變身:Rich 套件教學
·2 分鐘· loading · loading
Python Rich Cli
Python Typing:讓你的程式碼更安全、更好維護
·4 分鐘· loading · loading
Python Typing Type Hints