Python——Typer工具的使用


Typer 整体介绍

  • Typer 是一款基于 Python 类型注解(Type Hints)的 CLI 开发库,被称为CLI 界的 FastAPI ,是 FastAPI 的同作者开发的“轻兄弟”库
  • 理解:Typer(是 CLI 开发库)的本质是将 Python 函数变成可以在 Terminal 直接调用的命令形式
  • Typer 基于 Click 构建,兼具代码简洁、自动生成帮助文档、终端自动补全、支持复杂子命令等特性,开发者仅需少量代码就能构建专业、易用的命令行工具,同时对新手极其友好
    • 注:Click 是 Python 生态中一款经典、成熟的命令行界面(CLI)开发框架,也是 Typer 的底层核心依赖——Typer 本质上是对 Click 的高级封装,基于 Click 实现了所有 CLI 核心能力,同时通过 Python 类型注解简化了 Click 的使用流程

前置补充:Click 介绍

  • Click 由 Pallets 团队开发(同 Flask 开发团队),是 Python 中最主流的 CLI 开发库之一,解决了 Python 内置 argparse 模块代码繁琐、体验不佳、扩展能力弱的问题,核心优势是:
    • 基于装饰器实现简洁的命令 / 参数定义
    • 原生支持子命令、选项 / 参数解析、终端补全
    • 兼容所有主流终端,支持跨平台(Windows/Linux/macOS)
    • 提供丰富的扩展能力(如进度条、密码输入、颜色输出)

Typer 核心优势

  • 基于 Python 原生类型注解,无需学习新语法,少量代码即可实现 CLI 功能
  • 自动生成 --help 帮助文档、终端自动补全(支持 Bash/Zsh/Fish/PowerShell),bool 类型参数自动生成 --xxx/--no-xxx 双选项
  • 可灵活扩展,从简单单命令到多层嵌套子命令,可随项目复杂度无缝升级
  • 无缝兼容 Python 代码,无需修改现有 Python 脚本,直接通过 typer 命令将普通函数转为 CLI 工具
  • 内置美观的错误提示(基于 Rich)、进度条、彩色输出,提升用户使用体验

Typer 安装

  • Typer 支持 Python 3.6+,推荐在虚拟环境中安装,执行以下命令即可:

    1
    pip install typer
  • 安装完成后会自动附带三个核心依赖:

    • Click :Python 经典 CLI 框架,Typer 的底层基础
    • Rich :实现美观的格式化输出、彩色错误提示
    • shellingham :自动检测当前终端类型,支持自动补全安装

Typer 使用简单示例

  • Typer 的使用分为无侵入式运行普通脚本显式使用 Typer 开发 两种方式,先从最简单的无侵入式开始
  • 示例来自:

无侵入式:普通脚本直接转 CLI

  • 无需在代码中引入 Typer,直接将普通带类型注解的 Python 函数转为 CLI 工具

  • Step 1,创建 main.py,编写普通函数:

    1
    2
    3
    # 仅需普通Python代码+类型注解,无Typer相关代码
    def main(name: str):
    print(f"Hello {name}!")
  • Step 2,通过 typer 命令运行:

    1
    2
    3
    4
    # 查看帮助
    typer main.py run --help
    # 传入参数运行
    typer main.py run 张三
  • Step 3,效果:自动识别 name 为必填字符串参数,缺失时会抛出美观的错误提示,无需手动处理参数解析

显式使用(入侵式):引入 Typer 开发

  • 在代码中引入 Typer,可直接通过 python 命令运行,更适合正式开发

  • 修改 main.py,仅需2行新增代码(导入+运行):

    1
    2
    3
    4
    5
    6
    7
    import typer  # 新增:导入Typer

    def main(name: str):
    print(f"Hello {name}!")

    if __name__ == "__main__":
    typer.run(main) # 新增:运行Typer应用
  • 直接通过 Python 运行,体验与原生 CLI 工具一致:

    1
    2
    3
    4
    # 查看帮助
    python main.py --help
    # 传入参数运行
    python main.py 张三

Typer 进阶用法:多子命令开发

  • 当 CLI 工具需要多个功能时,Typer 支持通过 @app.command() 装饰器创建子命令 ,类似 Git 的 git add/git commit 模式,结构清晰

基础多子命令代码示例

  • 创建包含 hello(问候)和 goodbye(告别)两个子命令的工具,goodbye 新增布尔可选参数 formal(正式模式):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import typer

    # 1. 创建Typer应用实例,作为CLI入口
    app = typer.Typer()

    # 2. 用@app.command()装饰函数,转为子命令
    @app.command()
    def hello(name: str):
    """子命令:普通问候"""
    print(f"Hello {name}!")

    @app.command()
    def goodbye(name: str, formal: bool = False):
    """子命令:告别,--formal 开启正式模式"""
    if formal:
    print(f"Goodbye Ms. {name}. Have a good day!")
    else:
    print(f"Bye {name}!")

    # 3. 运行应用
    if __name__ == "__main__":
    app()

基础多子命令运行与使用示例

  • 查看全局帮助(显示所有子命令):

    1
    python main.py --help
    • 会自动列出 hellogoodbye 两个子命令,以及 --install-completion(安装终端补全)等全局选项
  • 查看子命令帮助:

    1
    2
    # 查看goodbye子命令的帮助,会显示--formal/--no-formal选项
    python main.py goodbye --help
  • 执行子命令:

    1
    2
    3
    4
    # 普通告别
    python main.py goodbye 张三
    # 正式模式告别(bool参数自动生成--formal选项)
    python main.py goodbye --formal 张三

基础多子命令示例关键特性说明

  • 布尔参数自动优化 :定义 formal: bool = False 后,Typer 会自动生成 --formal(开启)和 --no-formal(关闭)两个选项,无需手动配置
  • 帮助文档自动生成 :函数的文档字符串("""注释""")会自动作为子命令的帮助说明,--help 中直接显示
  • 命令名省略规则 :仅单个命令时,可直接 python main.py 参数;多个子命令时,必须显式指定子命令名(如 python main.py hello 张三

Typer 核心语法:参数定义

  • Typer 完全基于Python 原生类型注解 定义 CLI 参数,无需学习额外的装饰器或语法,支持所有常见类型

基础类型参数

  • 直接通过类型注解定义,Typer 自动解析为 CLI 位置参数/选项:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @app.command()
    def demo(
    # 必填字符串参数(位置参数)
    name: str,
    # 可选整数参数,默认值10(选项参数)
    age: int = 10,
    # 布尔参数,默认False(自动生成--is-adult/--no-is-adult)
    is_adult: bool = False
    ):
    # 推荐使用 `typer.echo()` 替代 `print()`,支持彩色输出、跨终端兼容
    typer.echo(f"姓名:{name},年龄:{age},是否成年:{is_adult}")

常用参数类型

  • 除基础类型外,Typer 还支持以下常用类型,直接注解即可:
    • 列表List[str],接收多个参数
    • 路径Path,自动校验文件/目录是否存在
    • 枚举Enum,实现参数可选值限制
    • 文件typer.File(),直接读取文件对象
  • 示例(枚举+列表):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    from typing import List
    from enum import Enum
    import typer

    # 枚举:限制gender的可选值
    class Gender(str, Enum):
    MALE = "male"
    FEMALE = "female"

    @app.command()
    def user(
    name: str,
    gender: Gender, # 仅能传入male/female
    hobbies: List[str] = None # 接收多个爱好,如--hobbies 篮球 读书
    ):
    typer.echo(f"姓名:{name},性别:{gender},爱好:{hobbies}")

Typer 高级功能:子命令组与全局配置

  • 当 CLI 工具功能复杂时,可创建子命令组(如按模块拆分:db backup/db restore),并通过 @app.callback() 实现全局配置(如环境、全局参数)

Typer 子命令组示例(数据库工具)

  • 示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import typer

    # 全局应用入口
    app = typer.Typer(help="多功能CLI工具")

    # 创建子命令组:db(数据库相关操作)
    db_app = typer.Typer(help="数据库操作子命令组")
    # 将子命令组添加到全局应用,命令名为db
    app.add_typer(db_app, name="db")

    # db子命令组下的子命令:backup(备份)
    @db_app.command()
    def backup(path: str = "./backup.db"):
    typer.echo(f"正在备份数据库到:{path}")

    # db子命令组下的子命令:restore(恢复)
    @db_app.command()
    def restore(path: str = "./backup.db"):
    typer.echo(f"正在从{path}恢复数据库")

    # 全局子命令:无归属,直接在根目录
    @app.command()
    def version():
    typer.echo("CLI工具版本:v1.0.0")

    if __name__ == "__main__":
    app()
  • 运行:

    1
    2
    3
    4
    5
    6
    # 查看数据库子命令组帮助
    python main.py db --help
    # 执行数据库备份
    python main.py db backup --path D:/mydb.db
    # 执行全局版本命令
    python main.py version

Typer 全局配置(@app.callback())

  • 通过 @app.callback() 定义全局参数(如运行环境 env),所有子命令均可共享:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import typer
    from typer import Context

    app = typer.Typer(help="带全局配置的CLI工具")

    # 全局回调:定义全局参数,所有子命令生效,所有子命令执行前,都会先执行这个函数
    @app.callback()
    def global_config(ctx: Context, env: str = typer.Option("dev", help="运行环境:dev/prod/test")):
    # 将全局参数存入ctx,供子命令获取
    ctx.ensure_object(dict)
    ctx.obj["env"] = env # 注:所有子命令的 ctx 对象都可以传入 env 参数,并可通过 ctx.obj["env"] 获取到 env

    @app.command()
    def run(ctx: Context):
    # 获取全局配置的env
    env = ctx.obj["env"]
    typer.echo(f"在{env}环境中运行程序...")

    if __name__ == "__main__":
    app()
  • 运行:

    1
    2
    3
    4
    # 用默认dev环境运行
    python main.py run
    # 指定prod环境运行
    python main.py run --env prod

Typer 实用功能:终端补全与进度条

安装终端自动补全

  • Typer 支持一键安装终端自动补全,输入以下命令后按提示操作即可:

    1
    python main.py --install-completion
    • 支持 Bash、Zsh、Fish、PowerShell 等主流终端,安装后输入命令时按 Tab 即可自动补全子命令和参数

内置进度条

  • 处理耗时操作(如下载、批量处理)时,Typer 内置进度条功能,无需额外安装库,一行代码实现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import typer
    import time

    app = typer.Typer()

    @app.command()
    def download(total: int = 10):
    """模拟下载,显示进度条"""
    # 用 typer.progressbar 创建进度条
    with typer.progressbar(range(total), label="下载中") as progress:
    for i in progress:
    time.sleep(0.5) # 模拟耗时操作
    typer.echo("下载完成!")

    if __name__ == "__main__":
    app()