fire 是 Google 出的命令行生成库,思路和 click 那一类完全相反:不写装饰器,不声明参数,把普通函数交给它,函数签名自动变成命令行。
写过 click 的人都知道,一个带两个子命令的小工具,光参数声明就要占十几行。fire 的卖点是这段声明代码可以全省掉。这篇验证两件事:省下来的代码值不值,代价是什么。
三个函数就是三个命令
import fire
def hello(name, times=1, loud=False):
"""打招呼"""
msg = "你好 " + name
if loud: msg = msg.upper() + "!"
for _ in range(times): print(msg)
def add(a, b):
"""两数相加"""
print(a + b)
if __name__ == "__main__":
fire.Fire({"hello": hello, "add": add})
就这些。没有 @click.command(),没有 add_argument,也没有类型注解。传给 fire.Fire 一个字典,字典的键就是子命令名。
和 click、typer 比行数与启动
同样两个子命令(hello 带一个位置参数加两个选项,add 带两个整数),三份实现都跑 --help,各 5 次取中位:
| 实现 | 有效代码行数 | --help 耗时 |
|---|---|---|
| fire | 12 行 | 1326 毫秒 |
| click | 20 行 | 979 毫秒 |
| typer | 14 行 | 1422 毫秒 |
行数确实最少,但启动时间不是最优:比 click 慢了 347 毫秒。fire 启动时要反射函数的签名、构建命令树、再拼帮助文本,这套动作在纯 Python 里做,省不掉。
实际调用也一样,参数形态的自动转换倒是正常,两个字符串参数被正确当成整数算出了 7:
| 调用 | 耗时 | 输出 |
|---|---|---|
| hello 实测库 --times=2 | 1243 毫秒 | 两行「你好 实测库」 |
| add 3 4 | 1170 毫秒 | 7 |
| --help(顶层) | 1326 毫秒 | stdout 0 字节 / stderr 209 字节 |
| hello --help(单命令) | 未单独计时 | stdout 0 字节 / stderr 355 字节 |
第一个坑:帮助信息写到 stderr
这是实测里最意外的一处。执行 python cli_fire.py --help,stdout 是 0 字节,209 字节全在 stderr:
INFO: Showing help with the command 'cli_fire.py -- --help'.
NAME
cli_fire.py
SYNOPSIS
cli_fire.py COMMAND
COMMANDS
COMMAND is one of the following:
hello
add
后果很具体:fire ... --help | less 看到的是空白,fire ... --help > help.txt 拿到的是空文件。要接管道得写成 2>&1。文档里那个 -- --help 的写法,是它在提示你真正的帮助命令是加两个减号分隔符。
第二个坑:参数传错直接抛 traceback
把 --times 传成字符串,期待它报「参数类型不对」再给一段用法。实际输出是完整的 Python 异常栈:
File "cli_fire.py", line 8, in hello
for _ in range(times): print(msg)
~~~~~^^^^^^^
TypeError: 'str' object cannot be interpreted as an integer
退出码 1,没有任何用法提示。原因是 fire 把字符串原样传进了函数,类型检查发生在函数体里面,它根本插不上手。click 遇到这种情况会打印 Error: Invalid value for '--times' 再加一段 usage,用户知道该怎么改。
两种情况的差别在于:你的函数自己抛异常时,用户看到的是业务错误还是用法错误,全靠函数写得好不好。
第三个坑:没有 --version
$ python cli_fire.py --version
ERROR: Cannot find key: --version
Usage: cli_fire.py <command>
available commands:
hello
add
exit code: 2
它把 --version 当成一个不存在的子命令。要给工具加版本号,得自己定义一个 version() 函数塞进字典。
子命令的帮助也只能自己看
单看一个子命令同样别扭。python cli_fire.py hello --help 的 stdout 仍是 0 字节,355 字节全在 stderr:
INFO: Showing help with the command 'cli_fire.py hello -- --help'.
NAME
cli_fire.py hello - 打招呼
SYNOPSIS
cli_fire.py hello NAME <flags>
POSITIONAL ARGUMENTS
NAME
FLAGS
--times
--loud
内容本身是对的:位置参数和两个开关都列出来了,docstring 也被当成描述抓了过去。问题只在于流向,脚本里想把它当文本处理,必须显式合并 stderr。
什么时候用它
内部脚本、一次性工具。同事要跑一个数据处理脚本,参数就是几个文件名和开关,写 12 行比写 20 行省事,帮助面板难看也无所谓。
已经有现成的类和函数,想直接暴露成命令。fire 能接对象、模块、字典,不需要改原有代码结构,这点 click 做不到。
对外发布的工具不要用。帮助信息进 stderr、参数错误抛 traceback、没有版本号,这三条在外发场景里都是硬伤,用户会当成程序崩了。
没测的部分列一下,3 项:嵌套子命令(多级字典)的行为 / 从类实例暴露方法的可用性 / 与 argparse 的互操作。








