Google 開源的 Python 命令行庫(kù):深入 fire(二)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
一、前言
在上一篇文章中我們介紹了 fire的子命令、嵌套命令和屬性訪問等內(nèi)容,今天我們將繼續(xù)深入了解
fire的其他功能。
本系列文章默認(rèn)使用 Python 3 作為解釋器進(jìn)行講解。
若你仍在使用 Python 2,請(qǐng)注意兩者之間語(yǔ)法和庫(kù)的使用差異哦~
二、功能
2.1 最簡(jiǎn)命令實(shí)現(xiàn)
在上一節(jié)中,我們介紹了只要定義一個(gè)函數(shù)就可以實(shí)現(xiàn)命令行程序。比如:
import fire def english(): return 'Hello, fire!' def chinese(): return '你好,fire!' if __name__ == '__main__':
fire.Fire()
但這還不是最簡(jiǎn)單的實(shí)現(xiàn)方式,fire甚至允許你通過(guò)定義變量的方式來(lái)實(shí)現(xiàn)命令行!上面的例子可以寫成下面這種形式:
import fire
english = 'Hello, fire!' chinese = '你好,fire!' if __name__ == '__main__':
fire.Fire()
2.2 鏈?zhǔn)秸{(diào)用
在 Fire CLI中,你可以通過(guò)鏈?zhǔn)秸{(diào)用不斷地對(duì)上一個(gè)結(jié)果進(jìn)行處理。
想做到這一點(diǎn)也很簡(jiǎn)單,就是在實(shí)例方法中返回 self即可。
在下面的示例中,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的四則運(yùn)算命令,可鏈?zhǔn)秸{(diào)用 add、sub、mul和 div。
import fire class Calculator: def __init__(self): self.result = 0 self.express = '0' def __str__(self): return f'{self.express} = {self.result}' def add(self, x): self.result += x
self.express = f'{self.express}+{x}' return self def sub(self, x): self.result -= x
self.express = f'{self.express}-{x}' return self def mul(self, x): self.result *= x
self.express = f'({self.express})*{x}' return self def div(self, x): self.result /= x
self.express = f'({self.express})/{x}' return self if __name__ == '__main__':
fire.Fire(Calculator)
上述代碼中的 add、sub、mul、div分別對(duì)應(yīng)加、減、乘、除的邏輯,每個(gè)方法都接受 x參數(shù)作為參與運(yùn)算的數(shù)字,返回值均為 self,這樣就可以無(wú)限次地鏈?zhǔn)秸{(diào)用。在命令行中鏈?zhǔn)秸{(diào)用結(jié)束后,會(huì)最終調(diào)用到 __str__方法將結(jié)果打印出來(lái)。
其中,__str__在 fire中用來(lái)完成自定義序列化。如果不提供這個(gè)方法,在鏈?zhǔn)秸{(diào)用完成后將會(huì)打印幫助內(nèi)容。
比如,我們可以這么調(diào)用:
$ python calculator.py add 1 sub 2 mul 3 div 4
((+1-2)*3)/4 = -0.75
$ python calculator.py add 1 sub 2 mul 3 div 4 add 4 sub 3 mul 2 div 1
((((0+1-2)*3)/4+4-3)*2)/1 = 0.5
2.3 位置參數(shù)和選項(xiàng)參數(shù)
通過(guò)前面的介紹我們也都清楚了在 fire中不必顯式的定義位置參數(shù)或選項(xiàng)參數(shù)。
通過(guò)下面的例子,我們將細(xì)化兩類參數(shù)的使用:
import fire class Building(object): def __init__(self, name, stories=1): self.name = name
self.stories = stories def __str__(self): return f'name: {self.name}, stories: {self.stories}' def climb_stairs(self, stairs_per_story=10): yield self.name 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)
-
構(gòu)造函數(shù)中定義的參數(shù)(如 name和 stories)在命令行中僅為選項(xiàng)參數(shù)(如 --name和 --stories)。我們可以這么調(diào)用:
$ python example.py --name="Sherrerd Hall" --stories=3
-
構(gòu)造函數(shù)中定義的參數(shù)可在命令中放于任意位置。比如下面兩個(gè)調(diào)用都是可以的:
$ python example.py --name="Sherrerd Hall" climb-stairs --stairs-per-story 10
$ python example.py climb-stairs --stairs-per-story 10 --name="Sherrerd Hall"
-
構(gòu)造函數(shù)和普通方法中定義的默認(rèn)參數(shù)(如 stories),在命令行中是可選的。我們可以這么調(diào)用:
$ python example.py --name="Sherrerd Hall"
-
普通方法中定義的參數(shù)(如 stairs_per_story)在命令行中即可以是位置參數(shù),也可以是選項(xiàng)參數(shù)。我們可以這么調(diào)用:
# 作為位置參數(shù) $ python example.py --name="Sherrerd Hall" climb_stairs 10 # 作為選項(xiàng)參數(shù) $ python example.py --name="Sherrerd Hall" climb_stairs --stairs_per_story=10
-
選項(xiàng)參數(shù)中的橫杠(-)和下劃線(_)是等價(jià)的。因此也可以這么調(diào)用:
# 作為選項(xiàng)參數(shù) $ python example.py --name="Sherrerd Hall" climb_stairs --stairs-per-story=10
此外,fire還支持在函數(shù)中定義 *args和 **kwargs。
import fire def fargs(*args): return str(args) def fkwargs(**kwargs): return str(kwargs) if __name__ == '__main__':
fire.Fire()
-
函數(shù)中的 *args在命令行中為位置參數(shù)。我們可以這么調(diào)用:
$ python example.py fargs a b c
-
函數(shù)中的 **kwargs在命令行中為選項(xiàng)參數(shù)。我們可以這么調(diào)用:
$ python example.py fargs --a a1 --b b1 --c c1
-
通過(guò)分隔符 -可顯式告知分隔符后的為子命令,而非命令的參數(shù)。且看下面的示例:
# 沒有使用分隔符,upper 被作為位置參數(shù) $ python example.py fargs a b c upper
('a', 'b', 'c', 'upper') # 使用了分隔符,upper 被作為子命令 $ python example.py fargs a b c - upper
('A', 'B', 'C')
-
通過(guò) fire內(nèi)置的 --separator可以自定義分隔符,此選項(xiàng)參數(shù)需要跟在單獨(dú)的 --后面:
$ python example.py a b c X upper -- --separator=X
('A', 'B', 'C')
2.4 參數(shù)類型
在 fire中,參數(shù)的類型由其值決定,通過(guò)下面的簡(jiǎn)單代碼,我們可以看到給不同的值時(shí),fire會(huì)解析為什么類型:
import fire
fire.Fire(lambda obj: type(obj).__name__)
$ python example.py 10
int
$ 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
如果想傳遞字符串形式的數(shù)字,那就需要小心引號(hào)了,要么把引號(hào)引起來(lái),要么轉(zhuǎn)義引號(hào):
# 數(shù)字 10 $ python example.py 10
int # 沒有對(duì)引號(hào)處理,仍然是數(shù)字10 $ python example.py "10" int # 把引號(hào)引起來(lái),所以是字符串“10” $ python example.py '"10"' str # 另一種把引號(hào)引起來(lái)的形式 $ python example.py "'10'" str # 轉(zhuǎn)義引號(hào) $ python example.py \"10\"
str
考慮下更復(fù)雜的場(chǎng)景,如果傳遞的是字典,在字典中有字符串,那么也是要小心引號(hào)的:
# 推薦做法 $ python example.py '{"name": "David Bieber"}' dict # 也是可以的 $ python example.py {"name":'"David Bieber"'}
dict # 錯(cuò)誤,會(huì)被解析為字符串 $ python example.py {"name":"David Bieber"}
str # 錯(cuò)誤,不會(huì)作為單個(gè)參數(shù)(因?yàn)橹虚g有空格),報(bào)錯(cuò) $ python example.py {"name": "David Bieber"}
如果值為 True或 False將為視為布爾值,fire還支持通過(guò) --name將 name設(shè)為 True,或通過(guò) --noname將 name設(shè)為 False:
$ python example.py --obj=True
bool
$ python example.py --obj=False
bool
$ python example.py --obj
bool
$ python example.py --noobj
bool
2.5 Fire 內(nèi)置選項(xiàng)參數(shù)
Fire 內(nèi)置了一些選項(xiàng)參數(shù),以幫助我們更容易地使用命令行程序。若想使用內(nèi)置的選項(xiàng)功能,需要將選項(xiàng)參數(shù)跟在 --后,在上文中,我們介紹了 --separator參數(shù),除了它,fire還支持以下選項(xiàng)參數(shù):
-
command -- --help列出詳細(xì)的幫助信息
-
command -- --interactive進(jìn)入交互式模式
-
command -- --completion [shell]生成 CLI 程序的自動(dòng)補(bǔ)全腳本,以支持自動(dòng)補(bǔ)全
-
command -- --trace獲取命令的 Fire 追蹤以了解調(diào)用 Fire 后究竟發(fā)生了什么
-
command -- --verbose獲取包含私有成員在內(nèi)的詳情
三、小結(jié)
fire讓命令行程序的實(shí)現(xiàn)變得特別簡(jiǎn)單,本文著重介紹了它的鏈?zhǔn)秸{(diào)用、選項(xiàng)參數(shù)、位置參數(shù)、參數(shù)類型以及內(nèi)置選項(xiàng)參數(shù)。fire的概念并不多,真正踐行了“把簡(jiǎn)單留給他人,把復(fù)雜留給自己”的理念。