主要参考和对官方文档进行二次浓缩而成
虽然有个 click
库来完成该任务,但是发现 fire
更好用!
1. 引入原因
讲述为什么需要使用 fire 这个第三方库呢?
- 当我需要写一个或多个命令行脚本工具的时候,头脑中首先会想到使用
Python
标准库中自带的argparse
模块来完成该任务。但是写完之后,会发现argparse
又臭又长,很不美观。
import argparse
import requests
def scrape(url, timeout=10):
response = requests.get(url, timeout=timeout)
print(response.text)
parser = argparse.ArgumentParser(description='Web Scrape')
parser.add_argument('url', type=str, help='scrapt url address')
parser.add_argument('timeout', type=int, help='run wait timeout')
if __name__ == '__main__':
args = parser.parse_args()
scrape(args.url, args.timeout)
- 此时,此时忽然想到使用 pallets/click 这个第三方库,毕竟之前还做过 笔记 不是。起初使用的话,没有多大问题,但是程序复杂了之后,管理起来还是挺麻烦的,尤其是需要对类进行操作的时候,就更为难受了。
import click
import requests
@click.command()
@click.argument('url', type=click.STRING)
@click.argument('timeout', type=click.INT)
def scrape(url, timeout=10):
response = requests.get(url, timeout=timeout)
print(response.text)
if __name__ == '__main__':
scrape()
- 此时,就需要使用我们这里介绍的 fire 第三方库了。当使用了了之后,会发现其命令行变得非常简单,只需要两行代码就可以完成了。
import fire
import requests
def scrape(url, timeout=10):
response = requests.get(url, timeout=timeout)
print(response.text)
if __name__ == '__main__':
fire.Fire(scrape)
2. 简单介绍
Fire
是一个用于自动生成命令行接口(CLIs
)的库!
如果你仔细看官方文档的话,在特性这里,其实就想说一句话,那就是:如果你需要写命令行工具,那用我就对了!同时,从仓库地址可以看到是 Google
开源的工具,Start
数量已经是 Click
的两倍还要多了。
- 安装方式
# pip
$ pip3 install fire
# conda
$ conda install fire -c conda-forge
# source
$ python3 setup.py install
- 相关概念 - 创建命令行
Creating a CLI | Command | Notes |
---|---|---|
import | import fire |
|
Call | fire.Fire() |
Turns the current module into a Fire CLI. |
Call | fire.Fire(component) |
Turns component into a Fire CLI. |
- 相关概念 - 使用命令行
Using a CLI | Command | Notes |
---|---|---|
Help | command --help or command -- --help |
|
REPL | command -- --interactive |
Enters interactive mode. |
Separator | command -- --separator=X |
Sets separator to X . Default separator is - . |
Completion | command -- --completion [shell] |
Generates a completion script for the CLI. |
Trace | command -- --trace |
Gets a Fire trace for the command. |
Verbose | command -- --verbose |
3. 简单使用
介绍该工具的几种简单的使用方式!
- [1] 暴露一个命令 -
fire.Fire()
- 绑定所有对象
import fire
def hello(name):
return 'Hello {name}!'.format(name=name)
if __name__ == '__main__':
fire.Fire()
# 绑定所有对象
$ python example.py hello World
Hello World!
- [2] 暴露一个命令 -
fire.Fire(<fn>)
- 绑定指定对象
import fire
def hello(name):
return 'Hello {name}!'.format(name=name)
if __name__ == '__main__':
fire.Fire(hello)
# 绑定指定对象
$ python example.py World
Hello World!
- [3] 暴露一个命令 - Using a main - 间接调用
import fire
def hello(name):
return 'Hello {name}!'.format(name=name)
def main():
fire.Fire(hello)
if __name__ == '__main__':
main()
- [4] 暴露一个命令 - Fire Without Code Changes - 定义即可使用
def hello(name):
return 'Hello {name}!'.format(name=name)
# 定义即可使用
$ python -m fire example hello --name=World
Hello World!
# 定义即可使用
$ python -m fire example.py hello --name=World
Hello World!
- [1] 暴露多个命令 - fire.Fire() - 默认绑定所有对象
import fire
def add(x, y):
return x + y
def multiply(x, y):
return x * y
if __name__ == '__main__':
fire.Fire()
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
- [2] 暴露多个命令 - fire.Fire(dict) - 子命令重命名
import fire
def add(x, y):
return x + y
def multiply(x, y):
return x * y
if __name__ == '__main__':
fire.Fire({'add': add, 'multiply': multiply})
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
- [3] 暴露多个命令 - fire.Fire(object) - 对类实例进行使用
import fire
class Calculator(object):
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
if __name__ == '__main__':
calculator = Calculator()
fire.Fire(calculator)
$ python example.py add 10 20
30
$ python example.py multiply 10 20
200
- [4] 暴露多个命令 - fire.Fire(class) - 对类进行使用
import fire
class BrokenCalculator(object):
def __init__(self, offset=1):
self._offset = offset
def add(self, x, y):
return x + y + self._offset
def multiply(self, x, y):
return x * y + self._offset
if __name__ == '__main__':
fire.Fire(BrokenCalculator)
$ python example.py add 10 20
31
$ python example.py multiply 10 20
201
$ python example.py add 10 20 --offset=0
30
$ python example.py multiply 10 20 --offset=0
200
4. 进阶特性
介绍该工具的进阶一点的特性!
- [1] 命令分组 - 多个类分组合并
class IngestionStage(object):
def run(self):
return 'Ingesting! Nom nom nom...'
class DigestionStage(object):
def run(self, volume=1):
return ' '.join(['Burp!'] * volume)
def status(self):
return 'Satiated.'
class Pipeline(object):
def __init__(self):
self.ingestion = IngestionStage()
self.digestion = DigestionStage()
def run(self):
self.ingestion.run()
self.digestion.run()
return 'Pipeline complete'
if __name__ == '__main__':
fire.Fire(Pipeline)
$ python example.py run
Ingesting! Nom nom nom...
Burp!
$ python example.py ingestion run
Ingesting! Nom nom nom...
$ python example.py digestion run
Burp!
$ python example.py digestion status
Satiated.
- [2] 访问属性 - 只是访问一个属性
from airports import airports
import fire
class Airport(object):
def __init__(self, code):
self.code = code
self.name = dict(airports).get(self.code)
self.city = self.name.split(',')[0] if self.name else None
if __name__ == '__main__':
fire.Fire(Airport)
$ python example.py --code=JFK code
JFK
$ python example.py --code=SJC name
San Jose-Sunnyvale-Santa Clara, CA - Norman Y. Mineta San Jose International (SJC)
$ python example.py --code=ALB city
Albany-Schenectady-Troy
- [3] 改变函数回调 - 对回传的数据进行二次操作
import fire
class BinaryCanvas(object):
"""A canvas with which to make binary art, one bit at a time."""
def __init__(self, size=10):
self.pixels = [[0] * size for _ in range(size)]
self._size = size
self._row = 0 # The row of the cursor.
self._col = 0 # The column of the cursor.
def __str__(self):
return '\n'.join(' '.join(str(pixel) for pixel in row) for row in self.pixels)
def show(self):
print(self)
return self
def move(self, row, col):
self._row = row % self._size
self._col = col % self._size
return self
def on(self):
return self.set(1)
def off(self):
return self.set(0)
def set(self, value):
self.pixels[self._row][self._col] = value
return self
if __name__ == '__main__':
fire.Fire(BinaryCanvas)
$ python example.py --code=ALB city upper
ALBANY-SCHENECTADY-TROY
$ python example.py move 3 3 on move 3 6 on move 6 3 on move 6 6 on move 7 4 on move 7 5 on
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
- [4] 更简单的调用方式 - 真的简单
import fire
english = 'Hello World'
spanish = 'Hola Mundo'
fire.Fire()
$ python example.py english
Hello World
$ python example.py spanish
Hola Mundo
- [5] 直接调用 call 方法 - 通过 –name=value 传值
import fire
class Building(object):
def __init__(self, name, stories=1):
self.name = name
self.stories = stories
def climb_stairs(self, stairs_per_story=10):
for story in range(self.stories):
for stair in range(1, stairs_per_story):
yield stair
yield 'Phew!'
yield 'Done!'
if __name__ == '__main__':
fire.Fire(Building)
$ python example.py --name="Sherrerd Hall"
$ python example.py --name="Sherrerd Hall" --stories=3 climb_stairs 10
$ python example.py --name="Sherrerd Hall" climb_stairs --stairs_per_story=10
$ python example.py --name="Sherrerd Hall" climb_stairs --stairs-per-story 10
$ python example.py climb-stairs --stairs-per-story 10 --name="Sherrerd Hall"
- [6] 对于泛解析字典和数组的使用
import fire
def order_by_length(*items):
sorted_items = sorted(items, key=lambda item: (len(str(item)), str(item)))
return ' '.join(sorted_items)
if __name__ == '__main__':
fire.Fire(order_by_length)
$ python example.py dog cat elephant
cat dog elephant
$ python example.py dog cat elephant - upper
CAT DOG ELEPHANT
$ python example.py dog cat elephant upper
cat dog upper elephant
$ python example.py dog cat elephant X upper -- --separator=X
CAT DOG ELEPHANT
- [7] 参数传递 - 实参的类型由它们的值决定
import fire
fire.Fire(lambda obj: type(obj).__name__)
$ python example.py 10
int
$ python example.py "10"
int
$ python example.py '"10"'
str
$ python example.py 10.0
float
$ python example.py hello
str
$ python example.py '(1,2)'
tuple
$ python example.py [1,2]
list
$ python example.py True
bool
$ python example.py {name:David}
dict
$ python example.py --obj=True
bool
$ python example.py --obj=False
bool