Google 開源的 Python 命令列庫:深入 fire(一)
作者:HelloGitHub-Prodesire
HelloGitHub 的《講解開源專案》系列,專案地址:https://github.com/HelloGitHub-Team/Article
一、前言
在第一篇“初探 fire”的文章中,我們初步掌握了使用 fire
的簡單步驟,瞭解了它 Pythonic 的用法。
今天我們將深入瞭解 fire
的子命令、巢狀命令和屬性訪問功能。
本系列文章預設使用 Python 3 作為直譯器進行講解。
若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~
二、功能
2.1 子命令
使用 fire
實現子命令有多種方式:
2.1.1 定義若干函式,使用 fire.Fire()
實現子命令最簡單的方式就是定義若干個函式,每個函式名隱式就是子命令名稱,然後呼叫 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
關於如何識別引數型別,比如上述 add 10 20
10
和 20
是作為數字而非字串,我們會在下篇文章的引數解析章節中進行講解。
2.1.2 定義若干函式,使用 fire.Fire()
在 2.1.1
的版本中,會把所有函式都當做是子命令。有時我們可能只想把部分函式當做子命令,或者是希望子命令名稱和函式名稱不一樣。這個時候我們就可以通過字典物件顯式地告訴 fire
。
字典物件的形式為 {'子命令名稱': 函式}
,比如前面的示例中,我們希望最終的子命令為 add
和 mul
,那麼就可以這麼寫:
fire.Fire({
'add': add,
'mul': multiply,
})
然後我們就可以在命令列中這麼呼叫:
$ python example.py add 10 20 30 $ python example.py mul 10 20 200
2.1.3 定義類和方法,使用 fire.Fire()
定義類和方法的這種方式我們在上一篇文章中介紹過,它和定義函式的方式基本相同,只不過是用類的方式來組織。
然後將類例項化,並把例項化的物件多為 fire.Fire
的入參:
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)
2.1.4 定義類和方法,使用 fire.Fire()
和 2.1.3
中的唯一不同點是把類而非例項物件作為 fire.Fire
的入參:
fire.Fire(Calculator)
傳遞類和例項物件的基本作用是一樣的,但傳遞類還有一個額外的特性:如果建構函式中定義了引數,那麼這些引數都會作為整個命令列程式的選項引數。
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 --help
INFO: Showing help with the command 'example.py -- --help'.
NAME
example.py
SYNOPSIS
example.py <flags>
FLAGS
--offset=OFFSET
由此可見建構函式 BrokenCalculator.__init__(self, offset=1)
中的 offset
自動轉換為了命令列中的全域性選項引數 --offset
,且預設值為 1
。
我們可以在命令列中這麼呼叫:
$ 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
2.2 命令組/巢狀命令
想要實現巢狀命令,可將多個類組織起來,示例如下:
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()
if __name__ == '__main__':
fire.Fire(Pipeline)
在上面的示例中:
IngestionStage
實現了子命令run
DigestionStage
實現了子命令run
和status
Pipeline
的建構函式中將IngestionStage
例項化為ingestion
,將DigestionStage
例項化為digestion
,就將這兩個放到一個命令組中,因而支援了:ingestion run
digestion run
digestion status
Pipeline
實現了子命令run
因此整個命令列程式支援如下命令:
run
ingestion run
digestion run
digestion status
然後我們就可以在命令列中這麼呼叫:
$ 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.3 屬性訪問
屬性訪問
是 fire
相對於其他命令列庫來說一個比較獨特的功能。所謂訪問屬性是獲取預置的屬性所對應的值。
舉個例子,在命令列中指定 --code
來告知程式要查詢的程式編碼,然後希望通過 zipcode
屬性返回郵編,通過 city
屬性返回城市名。那麼屬性可實現為例項成員屬性:
import fire
cities = {
'hz': (310000, '杭州'),
'bj': (100000, '北京'),
}
class City(object):
def __init__(self, code):
info = cities.get(code)
self.zipcode = info[0] if info else None
self.city = info[1] if info else None
if __name__ == '__main__':
fire.Fire(City)
使用方式如下:
$ python example.py --code bj zipcode
100000
$ python example.py --code hz city
杭州
三、小結
使用 fire
實現子命令和巢狀命令相對於其他命令列庫來說都更加簡單清晰,不僅如此,fire
還提供了屬性訪問這種較為獨特的能力。
在下篇文章中,我們將進一步深入瞭解 fire
,介紹其鏈式函式呼叫、自定義序列化、引數解析、fire 選項等更加高階的功能。
『講解開源專案系列』——讓對開源專案感興趣的人不再畏懼、讓開源專案的發起者不再孤單。跟著我們的文章,你會發現程式設計的樂趣、使用和發現參與開源專案如此簡單。歡迎留言聯絡我們、加入我們,讓更多人愛上開源、貢獻開源