在Python语言中,开发命令行程序,最基本的方式是通过Python标准库argparse来进行命令行参数和各种交互定义。但相对来说还比较复杂,一些常用的场景也需要我们单独封装。
本文我们介绍另一个非常强大,也非常容易上手使用的python命令行工具库typer
,看看如何利用它来帮我们快速完成一个命令行应用的实现。
安装和基本使用
安装
typer
是一个由 热门开源web接口框架 FastAPI
推出的命令行实现工具。和其他 python 第三方库类似,安装很方便。
pip instal typer
基本用法
姿势一
编写一个最基本的hello world
程序user_typer.py
1
2
3
4
5
|
def main():
print("hello world!")
if __name__ == "__main__":
main()
|
直接通过typer
命令调用
运行:
Demo>typer use_typer.py run
hello world!
姿势二
也可以在代码中引入typer,然后用它的run
方法直接在程序中调用
1
2
3
4
5
6
7
|
import typer
def main():
print("hello world!")
if __name__ == "__main__":
typer.run(main)
|
运行:
Demo>python use_typer.py
hello world!
姿势三
或者将程序声明为一个应用,然后用注解指定应用的每一个支持的命令
1
2
3
4
5
6
7
8
9
|
import typer
app = typer.Typer()
@app.command()
def main(name):
print("hello world!")
if __name__ == "__main__":
app()
|
运行:
Demo>python use_typer.py
hello world!
而且运行 python user_typer.py --help
, 可以看到已经自动生成了必要的帮助说明。

这里我们在main函数中加了一个参数,可以看到typer
其实还会自动将对应的函数携带的参数解析为命令参数,无需重新定义。
命令帮助和默认值
当然作为命令行程序,命令的帮助信息定义是一个重要部分。
typer
也提供了很方便的添加命令和参数帮助的途径。
比如我们再添加一个命令
1
2
3
4
5
6
|
@app.command(help="问候指定的小伙伴")
def hello(name:Annotated[str,typer.Argument(help="被问候的伙伴姓名")]="城下秋草"):
"""
这里也可以直接作为命令帮助,和command的help参数作用一样,
但优先级没有command注解高
""" print(f"你好,{name}")
|
命令本身的帮助信息可以直接在command注解中通过help参数说明,或者也可以通过函数本身的docstring,typer也会把它解析为命令帮助。但如果command注解中已经指定,则会以command注解中指定的帮助信息为准
而指定的参数name
, 这里是通过python的类型扩展库,再通过type.Argument
的help参数来指定,并且同时还可以指定参数的默认值。

可选参数和参数名
这里我们通过type.Argument
指定的参数,其实是必填参数。那如果我们希望某一个参数是可选的呢? 这里typer
中是根据两个不同类型来区分。定义上来说,
- typer.Argument 是必选
- typer.Option 是可选
对应到帮助信息中的 Arguments
和 Options
两个不同的pannel。 但是否必须输入,其实是根据是否指定了对应参数的默认值来确认的,如果没有指定默认值,那么就代表这个参数是必须输入的。
另外 Option 和 Argument 的不同还在于 Option 是需要指定option名称, 也就是形如 --name
, --help
这用带 --
或者 -
短参数标记
默认 typer.Option
是直接将参数名转化为长标记,也可以自行指定其他别名。 同时也可以指定指定短标记
比如我们可以再增加一个参数 last_name
1
2
3
4
5
6
7
|
@app.command(help="问候指定的小伙伴")
def hello(last_name:Annotated[str,typer.Option("--last","-l",help="被问候的人员姓什么")],
name:Annotated[str,typer.Argument(help="被问候的伙伴名称")]="城下秋草"):
"""
这里也可以直接作为命令帮助,和command的help参数作用一样,
但优先级没有command注解高
""" print(f"你好,{name} {last_name}")
|

更丰富的界面增强
typer
除了在参数定义上的灵活简便外,对于交互界面也提供了很多增强
颜色和结构化数据的显示支持
比如针对命令行,不同颜色的信息区分,这里它利用了另一个第三方库 rich
, 可以简单地用rich库中的print方法替代掉python官方的简单打印语句,实现更丰富的颜色控制和结构化数据展示等
1
2
3
4
5
6
7
8
9
10
|
def main():
print("显示结构数据:")
print(data)
print("不同颜色和emoji:")
print("[bold red]警告![/bold red] [green]数据data[/green] 不匹配! :boom:")
if __name__ == "__main__":
typer.run(main)
|
执行效果

提示信息
typer.Option
可以支持转为提示模式,也就是对于需要的参数,可以提示输入
比如上面的例子,加上prompt=True
就会在没有输入参数时提示输入
1
2
|
@app.command(help="问候指定的小伙伴")
def hello(last_name:Annotated[str,typer.Option("--last","-l",help="被问候的人员姓什么", prompt=True)]
|
运行:

密码和信息隐藏
对于一些密码类的参数,还支持隐藏和确认的选项,比如我们增加一个passwd参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@app.command(help="问候指定的小伙伴")
def hello(last_name:Annotated[str,
typer.Option("--last",
"-l",
help="被问候的人员姓什么",
prompt=True)]="黄",
passwd:Annotated[str,typer.Option(prompt=True, confirmation_prompt=True, hide_input=True)]=None,
name:Annotated[str,typer.Argument(help="被问候的伙伴名称")]="城下秋草"):
"""
这里也可以直接作为命令帮助,和command的help参数作用一样,
但优先级没有command注解高
""" if passwd == "123":
print(f"你好,{name} {last_name}")
else:
print("[bold red]密码输入错误,不能展示![/bold red]")
|

进度条实现
typer还能比较方便地支持进度条类的信息交互,同样可以利用rich中progress的增强
1
2
3
4
5
6
7
8
9
10
11
|
from rich.progress import track
def running():
total = 0
for value in track(range(100), description="正在执行中...."):
# Fake processing time
time.sleep(0.01)
total += 1
print(f"已处理 {total} 任务")
if __name__ == "__main__":
typer.run(running)
|

多层命令嵌套
对于多层的命令嵌套,typer
当然也提供了良好的支持。这里是通过声明不同的应用,再利用add_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
28
29
30
31
32
33
34
35
36
|
import typer
app = typer.Typer()
items_app = typer.Typer()
app.add_typer(items_app, name="items")
users_app = typer.Typer()
app.add_typer(users_app, name="users")
@items_app.command("create")
def items_create(item: str):
print(f"Creating item: {item}")
@items_app.command("delete")
def items_delete(item: str):
print(f"Deleting item: {item}")
@items_app.command("sell")
def items_sell(item: str):
print(f"Selling item: {item}")
@users_app.command("create")
def users_create(user_name: str):
print(f"Creating user: {user_name}")
@users_app.command("delete")
def users_delete(user_name: str):
print(f"Deleting user: {user_name}")
if __name__ == "__main__":
app()
|
用--help
看一下这样处理的效果

以上就是关于命令行工具 typer
的用法梳理。