|
| 1 | +# Python 命令行之旅:初探 fire |
| 2 | + |
| 3 | +## 一、前言 |
| 4 | + |
| 5 | +在本系列前面所有文章中,我们分别介绍了 `argparse`、`docopt` 和 `click` 的主要功能和用法。它们各具特色,都能出色地完成命令行任务。`argparse` 是面向过程的,需要先设置解析器,再定义参数,再解析命令行,最后实现业务逻辑。`docopt` 先用声明式的语法定义出参数,再过程式地解析命令行和实现业务逻辑。`click` 则是用装饰器的方式进一步简化显式的命令调用逻辑,但仍然不够面向对象。 |
| 6 | + |
| 7 | +而今天要介绍的 [fire](https://github.com/google/python-fire) 则是用一种面向广义对象的方式来玩转命令行,这种对象可以是类、函数、字典、列表等,它更加灵活,也更加简单。 |
| 8 | + |
| 9 | +``` |
| 10 | +本系列文章默认使用 Python 3 作为解释器进行讲解。 |
| 11 | +若你仍在使用 Python 2,请注意两者之间语法和库的使用差异哦~ |
| 12 | +``` |
| 13 | + |
| 14 | +## 二、介绍 |
| 15 | + |
| 16 | +[fire](https://github.com/google/python-fire) 可以根据任何 Python 对象自动生成命令行接口。它有如下特性: |
| 17 | + |
| 18 | +- 能以简单的方式生成 CLI |
| 19 | +- 是一个开发和调试 Python 代码的实用工具 |
| 20 | +- 能将现存代码或别人的代码转换为 CLI |
| 21 | +- 使得在 Bash 和 Python 间的转换变得更容易 |
| 22 | +- 通过预先为 REPL 设置所需的模块和变量,使得实用 REPL 更加容易 |
| 23 | + |
| 24 | +通过如下命令可快速安装 `fire` 库: |
| 25 | + |
| 26 | +```bash |
| 27 | +pip install fire |
| 28 | +``` |
| 29 | + |
| 30 | +## 三、快速开始 |
| 31 | + |
| 32 | +回忆下使用 `argparse`、`docopt` 和 `click` 实现命令行程序的步骤: |
| 33 | + |
| 34 | +- 对于 `argparse` 来说,要先设置解析器,再定义参数,再解析命令行,最后实现业务逻辑 |
| 35 | +- 对于 `docopt` 来说,要先定义定义接口描述,再解析命令行,最后实现业务逻辑 |
| 36 | +- 对于 `click` 来说,就是实现业务逻辑和通过装饰器的方式定义参数 |
| 37 | + |
| 38 | +它们的实现步骤越来越简单,从四步简化到了两步。而今天的主角 `fire` 则是跟进一步,只需实现业务逻辑就够了。 |
| 39 | + |
| 40 | +这简直简单的不可思议,为什么这样做就够了?我们不妨考虑下 Python 中的函数,函数是不是可以对应一个命令行程序,而函数的参数可以对应命令行程序的参数和选项呢?再看看 Python 中的类,一个类是不是可以对应一个命令行程序,而类中的每个实例方法就可以对应子命令,实例方法中的参数就是对应子命令的参数和选项。 |
| 41 | + |
| 42 | +这么一想,理论上确实是可以实现的,我们不妨通过下面的示例来看看 `fire` 是如何让我们通过简单的方式实现命令行程序。 |
| 43 | + |
| 44 | +### 3.1 使用函数 |
| 45 | + |
| 46 | +来看这么一个例子: |
| 47 | + |
| 48 | +```python |
| 49 | +import fire |
| 50 | + |
| 51 | +def hello(name="World"): |
| 52 | + return 'Hello {name}!'.format(name=name) |
| 53 | + |
| 54 | +if __name__ == '__main__': |
| 55 | + fire.Fire(hello) |
| 56 | +``` |
| 57 | + |
| 58 | +在上述例子中定义一个 `hello` 函数,它接受 `name` 参数,并且有默认值 "World"。使用 `fire.Fire(hello)` 即可非常简单快速地实现命令功能,这个命令行就接受 `--name` 选项,不提供时使用默认值 "World",提供时就按提供的值来。 |
| 59 | + |
| 60 | +可在命令行中执行下列命令: |
| 61 | + |
| 62 | +```bash |
| 63 | +$ python hello.py |
| 64 | +Hello World! |
| 65 | +$ python hello.py --name=Prodesire |
| 66 | +Hello Prodesire! |
| 67 | +$ python hello.py --help |
| 68 | +INFO: Showing help with the command 'hello.py -- --help'. |
| 69 | + |
| 70 | +NAME |
| 71 | + hello.py |
| 72 | + |
| 73 | +SYNOPSIS |
| 74 | + hello.py <flags> |
| 75 | + |
| 76 | +FLAGS |
| 77 | + --name=NAME |
| 78 | +``` |
| 79 | + |
| 80 | +### 3.2 使用类 |
| 81 | + |
| 82 | +使用函数是最简单的方式,如果我们想以更有组织的方式来实现,比如使用类,`fire` 也是支持的。 |
| 83 | + |
| 84 | +```python |
| 85 | +import fire |
| 86 | + |
| 87 | +class Calculator(object): |
| 88 | + """A simple calculator class.""" |
| 89 | + |
| 90 | + def double(self, number): |
| 91 | + return 2 * number |
| 92 | + |
| 93 | + def triple(self, number): |
| 94 | + return 3 * number |
| 95 | + |
| 96 | +if __name__ == '__main__': |
| 97 | + fire.Fire(Calculator) |
| 98 | +``` |
| 99 | + |
| 100 | +在上述例子中定义一个 `Calculator` 类,它有两个实例方法 `double` 和 `triple`,并且都接受 `number` 参数,没有默认值。使用 `fire.Fire(Calculator)` 即可非常简单快速地实现命令功能,这个命令行支持两个子命令 `double` 和 `triple`,位置参数 `NUMBER` 或选项参数 `--number` |
| 101 | + |
| 102 | +可在命令行中执行下列命令: |
| 103 | + |
| 104 | +```bash |
| 105 | +$ python calculator.py double 10 |
| 106 | +20 |
| 107 | +$ python calculator.py triple --number=15 |
| 108 | +45 |
| 109 | +$ python calculator.py double --help |
| 110 | +INFO: Showing help with the command 'calculator.py double -- --help'. |
| 111 | + |
| 112 | +NAME |
| 113 | + calculator.py double |
| 114 | + |
| 115 | +SYNOPSIS |
| 116 | + calculator.py double NUMBER |
| 117 | + |
| 118 | +POSITIONAL ARGUMENTS |
| 119 | + NUMBER |
| 120 | + |
| 121 | +NOTES |
| 122 | + You can also use flags syntax for POSITIONAL ARGUMENTS |
| 123 | +``` |
| 124 | + |
| 125 | +## 四、小结 |
| 126 | + |
| 127 | +`fire` 的使用方式非常简单,定一个 Python 对象,剩下的就交给 `fire` 来处理,可谓是非常的 Pythonic,这也是它会如此受欢迎的原因。 |
| 128 | + |
| 129 | +除了上面展示的内容,`fire` 还支持更多种类的 Python 对象,也拥有很多强大的功能,我们将在接下来几节中逐步走近它。 |
0 commit comments