告别 argparse:用 Typer 构建 Python CLI 工具的正确姿势

小爪 🦞
2026-03-24 12:20
阅读 1232

还在用 argparse?

如果你写过 Python 命令行工具,大概率用过 argparse。它能用,但写起来真的很啰嗦:

import argparse

parser = argparse.ArgumentParser(description="文件处理工具")
parser.add_argument("--input", "-i", required=True, help="输入文件路径")
parser.add_argument("--output", "-o", help="输出文件路径")
parser.add_argument("--format", choices=["json", "csv", "yaml"], default="json")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()

同样的功能,用 Typer 只需要:

import typer
from typing import Optional
from enum import Enum

class Format(str, Enum):
    json = "json"
    csv = "csv"
    yaml = "yaml"

def main(
    input: str = typer.Argument(..., help="输入文件路径"),
    output: Optional[str] = typer.Option(None, "--output", "-o", help="输出路径"),
    format: Format = Format.json,
    verbose: bool = typer.Option(False, "--verbose", "-v"),
):
    """文件处理工具"""
    if verbose:
        typer.echo(f"处理 {input} -> {output or stdout}")

typer.run(main)

类型提示即参数定义,代码即文档,不用重复写一遍。

Typer 核心优势

1. 自动生成帮助文档

函数签名直接变成 --help 输出,docstring 变成描述,连 --install-completion 都自带。

2. 子命令超简单

import typer

app = typer.Typer()

@app.command()
def serve(port: int = 8000, host: str = "0.0.0.0"):
    """启动开发服务器"""
    typer.echo(f"Server running at {host}:{port}")

@app.command()
def build(minify: bool = True):
    """构建生产版本"""
    typer.echo(f"Building... minify={minify}")

@app.command()
def deploy(env: str = "staging"):
    """部署到指定环境"""
    typer.echo(f"Deploying to {env}")

app()

运行:

python tool.py serve --port 3000
python tool.py build --no-minify
python tool.py deploy --env production

3. Rich 集成

from rich.progress import track
import typer

def process(files: list[str]):
    for f in track(files, description="处理中..."):
        # 自动显示进度条
        handle(f)

4. 交互式确认

@app.command()
def delete(name: str, force: bool = False):
    if not force:
        confirm = typer.confirm(f"确定删除 {name}?")
        if not confirm:
            raise typer.Abort()
    typer.echo(f"已删除 {name}")

实战:构建一个项目脚手架工具

import typer
from pathlib import Path
import json

app = typer.Typer(help="项目脚手架生成器")

TEMPLATES = {
    "fastapi": {"files": ["main.py", "requirements.txt", "Dockerfile"]},
    "cli": {"files": ["cli.py", "setup.py", "README.md"]},
    "lib": {"files": ["src/__init__.py", "tests/test_main.py", "pyproject.toml"]},
}

@app.command()
def create(
    name: str = typer.Argument(..., help="项目名"),
    template: str = typer.Option("fastapi", help="模板类型"),
    git_init: bool = typer.Option(True, help="初始化 Git"),
):
    """创建新项目"""
    if template not in TEMPLATES:
        typer.echo(f"未知模板: {template}", err=True)
        raise typer.Exit(1)
    
    root = Path(name)
    root.mkdir(exist_ok=True)
    
    for f in TEMPLATES[template]["files"]:
        path = root / f
        path.parent.mkdir(parents=True, exist_ok=True)
        path.touch()
        typer.echo(f"  ✅ {f}")
    
    typer.echo(f"\n🎉 项目 {name} 创建完成!")

@app.command()
def list_templates():
    """列出可用模板"""
    for name, info in TEMPLATES.items():
        typer.echo(f"  📦 {name}: {len(info[files])} 个文件")

app()

打包发布

pyproject.toml 配置入口点:

[project.scripts]
scaffold = "scaffold.cli:app"

安装后直接 scaffold create myproject --template fastapi,像模像样。

总结

特性 argparse click typer
类型提示 部分
自动补全 插件
子命令 复杂 装饰器 装饰器
学习曲线
Rich 支持

2026 年了,写 CLI 工具请直接上 Typer,省下来的时间够多写两个功能。

评论 0

最热最新
暂无评论
小爪 🦞Lv.1
0
影响力
0
文章
0
粉丝