Typer 入门指南:基于类型注解零样板构建你的第一个 Python CLI

发布时间:2026/9/13 19:07:21
Typer 入门指南:基于类型注解零样板构建你的第一个 Python CLI Typer 入门指南基于类型注解零样板构建你的第一个 Python CLI【免费下载链接】typerTyper, build great CLIs. Easy to code. Based on Python type hints.项目地址: https://gitcode.com/GitHub_Trending/ty/typer本篇技术指南以 Typer 官方教程《First Steps》为骨架带你从零构建第一个基于 Python 类型注解的 CLI 应用。文章完整覆盖最简单的 Typer 脚本、CLI 参数argument与 CLI 选项option的核心区别、必填/可选参数的默认行为、布尔开关的--formal/--no-formal自动生成、带值选项以及docstring 文档化等全部实战要素并结合本仓库的 核心实现源码 与 官方测试用例 深入讲解底层原理。读完本文你将能够独立用 Typer 编写出具备参数校验、帮助文档与自动补全能力的命令行程序。环境准备激活虚拟环境本文所有示例均基于本仓库的docs_src/first_steps/目录下的tutorial001_py310.py到tutorial006_py310.py六个完整脚本。在动手之前请先激活项目的虚拟环境并确保 Typer 已正确安装。激活后项目内安装的typer命令也会直接可用供后续小节使用。最简单的 Typer 程序Typer 最核心的设计理念是你只写普通的 Python 函数Typer 根据函数签名与类型注解自动生成 CLI。最简单的一个 Typer 文件如下完整代码见 tutorial001_py310.pyimport typer def main(): print(Hello World) if __name__ __main__: typer.run(main)将其复制保存为main.py后测试$ uv run python main.py Hello World // 再看看 --help $ uv run python main.py --help Usage: main.py [OPTIONS] ╭─ Options ─────────────────────────────────────────╮ │ --help Show this message and exit. │ ╰───────────────────────────────────────────────────╯这里发生了什么从源码角度看typer.run(main)的完整实现位于 typer/main.pydef run(function: Callable[..., Any]) - None: app Typer(add_completionFalse) app.command()(function) app()即run()实际做了三件事创建一个Typer()应用并关闭补全子命令、把传入的函数注册为命令、然后执行整个应用。这就是函数即命令魔法的最底层入口。该示例的官方测试见 test_tutorial001.py其中test_cli用CliRunner调用应用并断言输出恰为Hello World\n。不过这个程序还不够实用下面我们逐步为它添加参数。什么是 CLI 参数argument在 Typer 的语境中CLI 参数CLI argument指的是按特定顺序传给 CLI 应用的命令行参数默认情况下它们是必填的。用终端里的ls命令来直观理解。在终端输入$ ls ./myproject first-steps.md intro.mdls是程序也叫命令、CLI 应用./myproject是一个 CLI 参数在这里它指代一个目录的路径。CLI 参数与后面会讲到的 CLI 选项option有所不同。添加一个 CLI 参数现在给上一个例子添加名为name的参数完整代码见 tutorial002_py310.pyimport typer def main(name: str): print(fHello {name}) if __name__ __main__: typer.run(main)运行效果$ uv run python main.py // 不带参数运行时会给出友好错误 Usage: main.py [OPTIONS] {name} Try main.py --help for help. ╭─ Error ───────────────────────────────────────────╮ │ Missing argument name. │ ╰───────────────────────────────────────────────────╯ // 传入 name 参数 $ uv run python main.py Camila Hello Camila // Camila 就是这里的 CLI 参数 // 参数值含空格时用引号包裹 $ uv run python main.py Camila Gutiérrez Hello Camila Gutiérrez注意一个实用技巧如果某个 CLI 参数的值本身包含空格需要用引号将它整体包裹否则 shell 会把它拆成多个参数。两个 CLI 参数与顺序问题假设我们想把名和姓分开接收就把它扩展成name和lastname两个参数完整代码见 tutorial003_py310.pyimport typer def main(name: str, lastname: str): print(fHello {name} {lastname}) if __name__ __main__: typer.run(main)查看帮助信息$ uv run python main.py --help Usage: main.py [OPTIONS] {name} {lastname} ╭─ Arguments ───────────────────────────────────────╮ │ * name str [required] │ │ * lastname str [required] │ ╰───────────────────────────────────────────────────╯ ╭─ Options ─────────────────────────────────────────╮ │ --help Show this message and exit. │ ╰───────────────────────────────────────────────────╯现在帮助里出现了两个带*标记、标注[required]的 CLI 参数。如果只传一个$ uv run python main.py Camila Usage: main.py [OPTIONS] {name} {lastname} Try main.py --help for help. ╭─ Error ───────────────────────────────────────────╮ │ Missing argument lastname. │ ╰───────────────────────────────────────────────────╯由于两个参数都是必填的必须都传$ uv run python main.py Camila Gutiérrez Hello Camila Gutiérrez顺序至关重要姓必须跟在名之后。如果写成uv run python main.py Gutiérrez Camila应用无从得知哪个是name、哪个是lastname——它约定第一个 CLI 参数是name第二个是lastname。什么是 CLI 选项optionCLI 选项CLI option指的是带着特定名称传给 CLI 应用的命令行参数。还是用ls举例$ ls ./myproject --size 12 first-steps.md 4 intro.mdls是程序./myproject是一个 CLI 参数--size是一个可选的 CLI 选项。程序看到--size就知道要显示大小这与位置顺序无关。CLI 选项不像 CLI 参数那样依赖顺序所以把它放到参数前面也同样生效这也是最常用的写法$ ls --size ./myproject 12 first-steps.md 4 intro.mdCLI 选项与 CLI 参数最直观的区别是CLI 选项的名字前带有--前缀例如--size。为什么选项不依赖顺序因为 CLI 应用会主动寻找名为--size的文本也叫 flag 或 switch它会检查你是否输入了它——即使你没输入它也会去确认它是否存在。相比之下应用不会主动寻找文本为./myproject的参数它无法预知你会输入./myproject、./my-super-awesome-project还是其他任何东西它只是被动地接收你给的第一个位置值。判断某个 CLI 参数归属的唯一依据就是顺序。另外默认情况下 CLI 选项是可选的非必填。因此默认规则总结为CLI 参数argument默认必填requiredCLI 选项option默认可选optional当然必填与可选的默认值都是可以修改的。所以两者最核心的区别是CLI 选项以--开头且不依赖顺序CLI 参数依赖排列顺序补充一点上面的--size只是一个不含值的 flag/switch对应一个布尔值True或False取决于它是否出现在命令中。但 CLI 选项也可以像 CLI 参数一样接收值稍后你就会看到。添加一个 CLI 选项布尔开关现在给程序加上--formal选项完整代码见 tutorial004_py310.pyimport typer def main(name: str, lastname: str, formal: bool False): if formal: print(fGood day Ms. {name} {lastname}.) else: print(fHello {name} {lastname}) if __name__ __main__: typer.run(main)这里的formal是一个默认值为False的bool。查看帮助$ uv run python main.py --help Usage: main.py [OPTIONS] {name} {lastname} ╭─ Arguments ───────────────────────────────────────╮ │ * name str [required] │ │ * lastname str [required] │ ╰───────────────────────────────────────────────────╯ ╭─ Options ─────────────────────────────────────────╮ │ --formal --no-formal [default: no-formal] │ │ --help Show this message and exit. │ ╰───────────────────────────────────────────────────╯注意观察因为formal是bool类型Typer 自动同时生成了--formal与--no-formal两个选项。这背后的实现位于 typer/core.py当参数被识别为TyperOption且is_bool_flag为真、同时配置了secondary_opts时会把默认值对应的那个选项名如--no-formal写入帮助的默认值提示而secondary_opts的生成逻辑见 typer/core.py它会根据布尔参数的取值自动构造出--no-xxx形式的名字。也就是说布尔参数在 Typer 中被建模为成对出现的两个 flag。正常运行$ uv run python main.py Camila Gutiérrez Hello Camila Gutiérrez // 加上 --formal $ uv run python main.py Camila Gutiérrez --formal Good day Ms. Camila Gutiérrez. // --formal 是 CLI 选项可以放在命令的任意位置 $ uv run python main.py Camila --formal Gutiérrez Good day Ms. Camila Gutiérrez. $ uv run python main.py --formal Camila Gutiérrez Good day Ms. Camila Gutiérrez.最后三种写法结果完全一致这正是选项不依赖顺序的体现。官方测试 test_tutorial004.py 中的test_formal_1/2/3分别验证了这三种调用方式都断言输出Good day Ms. Camila Gutiérrez.同时test_help断言帮助文本里同时存在--formal和--no-formal。带值的 CLI 选项把lastname从一个 CLI 参数变成一个 CLI 选项只需要给它一个默认值完整代码见 tutorial005_py310.pyimport typer def main(name: str, lastname: str , formal: bool False): if formal: print(fGood day Ms. {name} {lastname}.) else: print(fHello {name} {lastname}) if __name__ __main__: typer.run(main)由于lastname现在有了默认值空字符串它在函数中不再是必填参数Typer 便默认把它转换成一个可选的 CLI 选项$ uv run python main.py --help Usage: main.py [OPTIONS] {name} ╭─ Arguments ───────────────────────────────────────╮ │ * name str [required] │ ╰───────────────────────────────────────────────────╯ ╭─ Options ─────────────────────────────────────────╮ │ --lastname str │ │ --formal --no-formal [default: no-formal] │ │ --help Show this message and exit. │ ╰───────────────────────────────────────────────────╯注意新出现的--lastname它会接收一个文本值。与--formal、--size这类无值的布尔 flag 不同带值的 CLI 选项如--lastname把紧跟在它右侧的值作为自己的取值。实际调用// 不传 --lastname使用默认空字符串 $ uv run python main.py Camila Hello Camila // 传入 --lastname $ uv run python main.py Camila --lastname Gutiérrez Hello Camila GutiérrezGutiérrez 位于--lastname的右侧正是它取值的位置。同样因为--lastname现在是选项顺序不再受限可以先传它再传 name$ uv run python main.py --lastname Gutiérrez Camila // 依然正常 Hello Camila Gutiérrez这个小例子揭示了一条重要规律Python 函数参数有无默认值直接决定了 Typer 把它映射为必填的 CLI 参数还是可选的 CLI 选项。用 docstring 文档化你的 CLI 应用给函数加上 docstring函数体内第一个、且不赋给任何变量的多行字符串它就会出现在帮助文本中完整代码见 tutorial006_py310.pyimport typer def main(name: str, lastname: str , formal: bool False): Say hi to name, optionally with a --lastname. If --formal is used, say hi very formally. if formal: print(fGood day Ms. {name} {lastname}.) else: print(fHello {name} {lastname}) if __name__ __main__: typer.run(main)再看--help$ uv run python main.py --help Usage: main.py [OPTIONS] {name} Say hi to name, optionally with a --lastname. If --formal is used, say hi very formally. ╭─ Arguments ───────────────────────────────────────╮ │ * name str [required] │ ╰───────────────────────────────────────────────────╯ ╭─ Options ─────────────────────────────────────────╮ │ --lastname str │ │ --formal --no-formal [default: no-formal] │ │ --help Show this message and exit. │ ╰───────────────────────────────────────────────────╯docstring 的内容被展示在 Usage 行下方、参数表格上方作为整个命令的说明文字。此外还有一种更细粒度的文档化方式为具体的某个 CLI 选项或参数单独写帮助说明它会显示在对应选项/参数旁边类似内置的--help那样这部分内容将在教程后面的章节中学习。术语辨析arguments、options、parameters、optional、required这些术语在不同的语境下指代不同的事物而这些语境经常混在一起非常容易混淆。下面分三层厘清。在 Python 中函数里的变量名比如name和lastnamedef main(name: str, lastname: str ): pass被称为Python 函数参数Python function parameters或Python 函数实参Python function arguments。技术细节Python 中parameter与argument其实有细微差别比较学究气Parameter指函数声明中的变量名如def bring_person(name: str, lastname: str ):中的name、lastnameArgument指调用函数时传入的值如person bring_person(Camila, lastnameGutiérrez)中的Camila。不过大多数场合包括本文档会混用这两个词。Python 的默认值在 Python 中带默认值的参数如上面的lastname被称为可选参数optional parameter/argument默认值可以是任何东西比如或None而没有默认值的参数如name则被视为必填required。在 CLI 中讨论命令行应用时argument和parameter通常都指传给 CLI 应用的数据。但这两个词并不隐含数据是否必填、是否需要按顺序传、以及是否带有--lastname这样的 flag。带名称如--lastname可附带一个值的参数通常可选、非必填因此在 CLI 语境下常被称为可选参数optional arguments/parameters有时这些以--开头的可选参数也被叫做flag或switch。不过实际中需要顺序的参数也可以做成可选的带 flag如--lastname的参数也可以做成必填的。在 Typer 中为了让事情更清晰Typer 约定如下parameter或argument——指 Python 函数的参数/实参CLI argument——指依赖特定顺序的 CLI 参数默认必填CLI option——指依赖--开头名称如--lastname的 CLI 参数默认可选CLI parameter——是两者的统称既包括 CLI 参数也包括 CLI 选项。使用typer命令脚本也能享受自动补全当你激活项目的虚拟环境后项目里安装的typer命令就会直接出现在你的 shell 中。为它安装补全后就可以用这个命令运行你的脚本并获得 ✨ 终端自动补全 ✨ 能力。除了用 Python 直接运行$ uv run python main.py Hello World还可以用typer命令运行$ typer main.py run Hello World之后在终端中按TAB就能对脚本里的所有代码获得自动补全。在继续后续教程时可以用它来为你的脚本提供补全体验。需要说明的是一旦你把应用打包成 Python 包基于 Typer 构建的 CLI 应用本身就不再依赖typer命令即可获得补全。但对于短小的脚本、在学习阶段尚未创建 Python 包时typer命令是个很有用的工具。小结与下一步回顾本文你已完成 Typer 的六个渐进式示例示例脚本核心知识点tutorial001_py310.pytyper.run()最小应用与自动--helptutorial002_py310.py单个必填 CLI 参数缺参时报错tutorial003_py310.py多参数按顺序传参tutorial004_py310.py布尔 flag 自动生成--formal/--no-formaltutorial005_py310.py默认值让参数变成带值的可选 CLI 选项tutorial006_py310.pydocstring 自动进入帮助文本在此基础上可以继续阅读 CLI 参数进阶、CLI 选项进阶、参数类型 等章节进一步掌握枚举、路径、UUID、环境变量等丰富能力把入门示例扩展成真正可交付的命令行工具。【免费下载链接】typerTyper, build great CLIs. Easy to code. Based on Python type hints.项目地址: https://gitcode.com/GitHub_Trending/ty/typer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询