本文 AI 產出,尚未審核
Python – 自動化與腳本應用(Automation & CLI)
主題:argparse 與 click 模組
簡介
在日常的開發與維運工作中,命令列介面 (CLI) 是最常見的自動化入口。無論是資料處理、測試腳本,或是部署工具,都需要透過參數、選項與子指令讓使用者靈活控制程式行為。Python 內建的 argparse 與第三方的 click,是兩套最受歡迎的 CLI 建構套件。
argparse於 Python 2.7 之後即成標準庫,適合不想額外安裝套件且需求相對簡單的情境。click則以「Command Line Interface Creation Kit」為名,提供更直覺的裝飾器語法、子指令支援以及自動產生的說明文件,常被用於較大型的工具或開源專案。
掌握這兩個模組的 核心概念、常見陷阱 與 最佳實踐,能讓你的腳本從「只能跑」升級為「好用又易維護」的專業工具。
核心概念
1. argparse 的基本流程
- 建立
ArgumentParser物件:設定程式說明、預設的錯誤處理方式。 - 定義參數:使用
add_argument()加入位置參數、選項參數或旗標。 - 解析參數:呼叫
parse_args(),返回一個屬性化的 Namespace 物件。
範例 1:最簡單的「Hello」程式
import argparse
parser = argparse.ArgumentParser(description="簡易問候程式")
parser.add_argument("name", help="要問候的對象")
args = parser.parse_args()
print(f"Hello, {args.name}!")
name為 位置參數,必須出現在指令的最後。- 執行
python hello.py Alice後會輸出Hello, Alice!。
範例 2:加入選項與預設值
import argparse
parser = argparse.ArgumentParser(description="計算兩數之和")
parser.add_argument("a", type=int, help="第一個整數")
parser.add_argument("b", type=int, help="第二個整數")
parser.add_argument("-v", "--verbose", action="store_true",
help="顯示詳細計算過程")
args = parser.parse_args()
result = args.a + args.b
if args.verbose:
print(f"{args.a} + {args.b} = {result}")
else:
print(result)
-v/--verbose為 旗標,使用store_true讓它在出現時回傳True。type=int讓argparse自動把字串轉成整數,若轉換失敗會顯示友善的錯誤訊息。
範例 3:支援子指令(sub‑commands)
import argparse
parser = argparse.ArgumentParser(description="檔案管理小工具")
subparsers = parser.add_subparsers(dest="command", required=True)
# copy 子指令
cp_parser = subparsers.add_parser("copy", help="複製檔案")
cp_parser.add_argument("src", help="來源檔案")
cp_parser.add_argument("dst", help="目標檔案")
# delete 子指令
rm_parser = subparsers.add_parser("delete", help="刪除檔案")
rm_parser.add_argument("target", help="要刪除的檔案")
rm_parser.add_argument("-f", "--force", action="store_true",
help="強制刪除,不詢問")
args = parser.parse_args()
if args.command == "copy":
print(f"Copy {args.src} → {args.dst}")
elif args.command == "delete":
print(f"Delete {args.target}, force={args.force}")
add_subparsers()讓程式支援 多個子指令,每個子指令都有自己的參數集合。dest="command"把子指令名稱存入args.command,required=True確保必須指定子指令。
2. click 的設計哲學
click 以 裝飾器(decorator) 為核心,將 CLI 定義與函式本身緊密結合,讓程式碼更具可讀性。它的特點包括:
- 自動產生
--help:不需要額外撰寫說明文件。 - 支援型別轉換、預設值與回呼(callback)。
- 支援多層子指令,且可以輕鬆加入 環境變數、設定檔 等進階功能。
範例 4:最基礎的 Click 程式
import click
@click.command()
@click.argument('name')
def hello(name):
"""簡易問候程式"""
click.echo(f'Hello, {name}!')
if __name__ == '__main__':
hello()
@click.command()宣告這是一個 CLI 命令。@click.argument('name')定義位置參數。click.echo()取代內建的print(),自動處理 Unicode 與流向。
範例 5:選項、型別與預設值
import click
@click.command()
@click.option('-a', '--a', type=int, required=True, help='第一個整數')
@click.option('-b', '--b', type=int, required=True, help='第二個整數')
@click.option('-v', '--verbose', is_flag=True, help='顯示詳細資訊')
def add(a, b, verbose):
"""計算兩數之和"""
result = a + b
if verbose:
click.echo(f'{a} + {b} = {result}')
else:
click.echo(result)
if __name__ == '__main__':
add()
is_flag=True讓--verbose成為布林旗標。type=int直接在 Click 內完成型別轉換,若錯誤會自動拋出BadParameter。
範例 6:多層子指令(Group)
import click
import shutil
import os
@click.group()
def cli():
"""檔案管理小工具"""
pass
@cli.command()
@click.argument('src', type=click.Path(exists=True))
@click.argument('dst', type=click.Path())
def copy(src, dst):
"""複製檔案"""
shutil.copy(src, dst)
click.echo(f'Copy {src} → {dst}')
@cli.command()
@click.argument('target', type=click.Path(exists=True))
@click.option('-f', '--force', is_flag=True, help='強制刪除')
def delete(target, force):
"""刪除檔案"""
if not force and not click.confirm(f'確定要刪除 {target}?'):
click.echo('已取消')
return
os.remove(target)
click.echo(f'Deleted {target}')
if __name__ == '__main__':
cli()
@click.group()建立一個 指令群組,子指令使用@cli.command()註冊。click.Path()可檢查檔案是否存在、是否為檔案或目錄,減少手動驗證的程式碼。
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 解決方式 / 最佳實踐 |
|---|---|---|
混用 argparse 與 click |
兩套解析器會爭奪 sys.argv,導致錯誤或無法產生 --help。 |
選擇其一:若專案已大量使用 click,就全程使用;若只需要簡單參數,直接使用 argparse。 |
忘記設定 required=True(在 argparse) |
位置參數缺失時會產生不易理解的 SystemExit。 |
明確標示必填參數,或自行檢查 None 後給予友善訊息。 |
過度使用 store_true 而忽略預設值(argparse) |
使用者未提供旗標時,程式無法辨識「未設定」與「設定為 False」的差異。 | 若需要三態(True / False / None),改用 action='store_const' 或自行處理。 |
子指令名稱衝突(click) |
兩個子指令同名會在執行時覆蓋,造成不可預期行為。 | 在 @click.group() 中使用 invoke_without_command=True,或在命名時加入前綴。 |
| 未處理例外 | 當使用者輸入不合法的路徑或型別時,腳本直接崩潰。 | 使用 try/except 包住關鍵區塊,並利用 click.ClickException 或 argparse.ArgumentTypeError 提供清晰訊息。 |
| 說明文件過長、未對齊 | --help 輸出難以閱讀。 |
使用 formatter_class=argparse.ArgumentDefaultsHelpFormatter(argparse)或 help_option_names=['-h', '--help'](click)統一格式。 |
最佳實踐總結:
- 先規劃參數結構:先畫出需求圖,決定是否需要子指令、旗標或環境變數。
- 盡量使用型別與預設值:讓解析器自動驗證,減少手動檢查。
- 保持說明文件一致:
argparse可使用description、epilog,click可在@click.command(help=...)中寫。 - 測試 CLI:利用
subprocess.run([...])或click.testing.CliRunner撰寫單元測試,確保參數變更不會破壞相容性。 - 考慮未來擴充:若預期會加入更多子指令,建議直接使用
click,因為其群組機制較為彈性。
實際應用場景
| 場景 | 推薦模組 | 為什麼 |
|---|---|---|
| 一次性資料清理腳本 | argparse |
需求簡單、只需幾個位置參數或旗標,無需額外依賴。 |
| CI/CD 工具(如自動部署、測試) | click |
多子指令(build、test、deploy)與豐富的選項、環境變數支援,使腳本可成為完整的 CLI 套件。 |
| 系統管理員的批次作業 | click + click.Path |
自動檢查檔案/目錄、提供互動式確認,降低誤操作風險。 |
| 教學或範例程式 | argparse |
內建模組,讓學員免安裝外部套件即可直接執行。 |
| 跨平台工具(Windows、Linux、macOS) | 任意,視需求而定;若要支援 自動補全(bash/zsh),click 提供 click_completion 擴充套件。 |
總結
argparse是 標準庫,適合輕量、一次性或教學用的 CLI;其核心概念是 建立解析器 → 定義參數 → 解析。click以 裝飾器 為核心,提供更直覺的子指令管理、型別驗證與自動化說明文件,特別適合 中大型工具。- 兩者的 共同要點:明確的參數規劃、充分的說明文件、妥善的錯誤處理與單元測試,都是打造 好用且可靠 CLI 的關鍵。
掌握上述概念與實務技巧後,你就能根據專案規模與需求,靈活選擇 argparse 或 click,讓 Python 腳本從「只能執行」升級為「使用者友好、易於維護」的專業自動化工具。祝你寫出更多得心應手的命令列程式!