Python 命令行之旅:深入 argparse(二)
作者:HelloGitHub-
Prodesire
HelloGitHub 的《講解開源項目》系列,項目地址:https://github.com/HelloGitHub-Team/Article
前言
在上一篇“深入 argparse(一)”的文章中,我們深入了解了
argparse
的包括參數動作和參數類別在內的基本功能,具備了編寫一個簡單命令行程序的能力。本文將繼續深入了解
argparse
的進階玩法,一窺探其全貌,助力我們擁有實現復雜命令行程序的能力。
本系列文章默認使用 Python 3 作為解釋器進行講解。
若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~
幫助
自動生成幫助
當你在命令行程序中指定
-h
或
--help
參數時,都會輸出幫助信息。而
argparse
可通過指定
add_help
入參為
True
或不指定,以達到自動輸出幫助信息的目的。
>>> import argparse
>>> parser = argparse.ArgumentParser(add_help=True)
>>> parser.add_argument('--foo')
>>> parser.parse_args(['-h'])
usage: [-h] [--foo FOO]
optional arguments:
-h, --help show this help message and exit
--foo FOO
如果
add_help=False
,那么在命令行中指定
-h
則會報錯:
>>> import argparse
>>> parser = argparse.ArgumentParser(add_help=False)
>>> parser.add_argument('--foo')
>>> parser.parse_args(['-h'])
usage: [--foo FOO]
: error: unrecognized arguments: -h
自定義幫助
ArgumentParser
使用
formatter_class
入參來控制所輸出的幫助格式。
比如,通過指定
formatter_class=argparse.RawTextHelpFormatter
,我們可以讓幫助內容遵循原始格式:
>>> import argparse
>>> parser = argparse.ArgumentParser(
... add_help=True,
... formatter_class=argparse.RawTextHelpFormatter,
... description="""
... description
... raw
... formatted"""
... )
>>> parser.add_argument(
... '-a', action="store_true",
... help="""argument
... raw
... formatted
... """
... )
>>>
>>> parser.parse_args(['-h'])
usage: [-h] [-a]
description
raw
formatted
optional arguments:
-h, --help show this help message and exit
-a argument
raw
formatted
對比下不指定
formatter_class
的幫助輸出,就可以發現 descirption 和 -a 兩個幫助內容上的差異:
>>> import argparse
>>> parser = argparse.ArgumentParser(
... add_help=True,
... description="""
... description
... notraw
... formatted"""
... )
>>> parser.add_argument(
... '-a', action="store_true",
... help="""argument
... notraw
... formatted
... """
... )
>>> parser.parse_args(['-h'])
usage: [-h] [-a]
description notraw formatted
optional arguments:
-h, --help show this help message and exit
-a argument notraw formatted
參數組
有時候,我們需要給參數分組,以使得在顯示幫助信息時能夠顯示到一起。
比如某命令行支持三個參數選項
--user
、
--password
和
--push
,前兩者需要放在一個名為
authentication
的分組中以表示它們是身份認證信息。那么我們可以用
ArgumentParser.add_argument_group
來滿足:
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> group = parser.add_argument_group('authentication')
>>> group.add_argument('--user', action="store")
>>> group.add_argument('--password', action="store")
>>> parser.add_argument('--push', action='store')
>>> parser.parse_args(['-h'])
usage: [-h] [--user USER] [--password PASSWORD] [--push PUSH]
optional arguments:
-h, --help show this help message and exit
--push PUSH
authentication:
--user USER
--password PASSWORD
可以看到,當我們輸出幫助信息時,
--user
和
--password
選項都出現在
authentication
分組中。
選項參數前綴
不知你是否注意到,在不同平臺上命令行程序的選項參數前綴可能是不同的。比如在 Unix 上,其前綴是
-
;而在 Windows 上,大多數命令行程序(比如
findstr
)的選項參數前綴是
/
。
在
argparse
中,選項參數前綴默認采用 Unix 命令行約定,也就是
-
。但它也支持自定義前綴,下面是一個例子:
>>> import argparse
>>>
>>> parser = argparse.ArgumentParser(
... description='Option prefix',
... prefix_chars='-+/',
... )
>>>
>>> parser.add_argument('-power', action="store_false",
... default=None,
... help='Set power off',
... )
>>> parser.add_argument('+power', action="store_true",
... default=None,
... help='Set power on',
... )
>>> parser.add_argument('/win',
... action="store_true",
... default=False)
>>> parser.parse_args(['-power'])
Namespace(power=False, win=False)
>>> parser.parse_args(['+power', '/win'])
Namespace(power=True, win=True)
在這個例子中,我們指定了三個選項參數前綴
-
、
+
和
/
,從而:
-
通過指定選項參數
-power
,使得power=False
-
通過指定選項參數
+power
,使得power=True
-
通過指定選項參數
/win
,使得win=True
共享解析器
有些時候我們需要共享解析器,以共享里面的參數配置。比如,我們的命令行工具需要支持對阿里云和 AWS 進行操作,兩類操作都需要指定
AccessKeyId
和
AccessKeySecret
來表明用戶身份和權限。那么共享解析器就顯得尤為必要,這樣就可以少去重復代碼。
我們可以這樣做,在
base.py
中定義一個父解析器,存放
AccessKey
相關參數配置,作為公用的解析器。由于后續的子解析器會自動生成幫助信息,這里的父解析器指定
add_help=False
以不自動生成幫助信息:
# bash.py
import argparse
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--ak-id', action="store")
parser.add_argument('--ak-secret', action="store")
然后就可以分別在
ali.py
和
aws.py
中分別定義子解析器,通過
parents
入參指定上述父解析器,從而繼承公共的參數,并實現各自的參數:
# ali.py
import argparse
import base
parser = argparse.ArgumentParser(
parents=[base.parser],
)
parser.add_argument('--ros',
action="store_true",
default=False,
help='Using ROS service to orchestrate cloud resources')
print(parser.parse_args())
# aws.py
import argparse
import base
parser = argparse.ArgumentParser(
parents=[base.parser],
)
parser.add_argument('--cloudformation',
action="store_true",
default=False,
help='Using CloudFormation service to orchestrate cloud resources')
print(parser.parse_args())
最終通過
-h
參數分別看
ali.py
和
aws.py
所支持的參數,其中共同參數為
--ak-id
和
--ak-secret
,特定參數分別為
--ros
和
--cloudformation
:
$ python3 ali.py -h
usage: ali.py [-h] [--ak-id AK_ID] [--ak-secret AK_SECRET] [--ros]
optional arguments:
-h, --help show this help message and exit
--ak-id AK_ID
--ak-secret AK_SECRET
--ros Using ROS service to orchestrate cloud resources
$ python3 aws.py -h
usage: aws.py [-h] [--ak-id AK_ID] [--ak-secret AK_SECRET] [--cloudformation]
optional arguments:
-h, --help show this help message and exit
--ak-id AK_ID
--ak-secret AK_SECRET
--cloudformation Using CloudFormation service to orchestrate cloud
resources
嵌套解析器
我們之前介紹的命令行中,使用形式通常是
cli --a --b xxx
。但還有一種極為常見的命令行使用方式是
cli subcmd --a --b xxx
。比如當我們要通過
git
推送標簽時,會用到
git push --tags
。
通過實現嵌套解析器,我們可以很容易地對這種子命令的形式進行解析。
在嵌套解析器中,我們定義一個父解析器來作為整個命令行的入口,再分別定義N個子解析器來對應N個子命令,由此即可實現整個功能。
在下面這個例子中,我們支持
create
和
delete
兩個子命令,用來創建或刪除指定路徑。而
delete
命令支持
--recursive
參數來表明是否遞歸刪除指定路徑:
# cli.py
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='commands')
# Create
create_parser = subparsers.add_parser(
'create', help='Create a directory')
create_parser.add_argument(
'dirname', action='store',
help='New directory to create')
# Delete
delete_parser = subparsers.add_parser(
'delete', help='Remove a directory')
delete_parser.add_argument(
'dirname', action='store', help='The directory to remove')
delete_parser.add_argument(
'--recursive', '-r', default=False, action='store_true',
help='Recursively remove the directory',
)
print(parser.parse_args())
直接指定
-h
來查看所支持的子命令和參數選項:
$ python3 cli.py -h
usage: cli.py [-h] {create,delete} ...
positional arguments:
{create,delete} commands
create Create a directory
delete Remove a directory
optional arguments:
-h, --help show this help message and exit
直接指定
delete -h
來查看
delete
子命令支持的參數選項:
$ python3 cli.py delete -h
usage: cli.py delete [-h] [--recursive] dirname
positional arguments:
dirname The directory to remove
optional arguments:
-h, --help show this help message and exit
--recursive, -r Recursively remove the directory
自定義動作
在上一篇“深入 argparse (一)”的文章中介紹過8種參數動作,可以說是覆蓋了絕大部分場景。但是也會有一些特定需求無法被滿足,比如希望獲取到的參數值都是大寫。在這種情況下,自定義動作就派上了用場。
實現一個自定義動作類,需繼承自
argparse.Action
,這個自定義動作類要傳入到
ArgumentParser.add_argument
的
action
入參。當解析器解析參數時,會調用該類的
__call__
方法,該方法的簽名為
__call__(self, parser, namespace, values, option_string=None)
,其中:
- parser 為解析器實例
- namespace 存放解析結果
- values 即命令行中傳入的參數值
- option_string 為參數選項
在下面的例子中,我們通過
--words
傳入單詞,并在自定義動作類中將其值轉換為大寫:
# cli.py
import argparse
class WordsAction(argparse.Action):
def __call__(self, parser, namespace, values,
option_string=None):
print(f'parser = {parser}')
print(f'values = {values!r}')
print(f'option_string = {option_string!r}')
values = [v.upper() for v in values]
setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser()
parser.add_argument('--words', nargs='*', action=WordsAction)
results = parser.parse_args()
print(results)
$ python3 cli.py --words foo bar
parser = ArgumentParser(prog='cli.py', usage=None, description=None, formatter_class=
, conflict_handler='error', add_help=True)
values = ['foo', 'bar']
option_string = '--words'
Namespace(words=['FOO', 'BAR'])
小節
通過對
argparse
由淺入深的介紹,相信你已經全面了解了
argparse
的威力,也具備了開發命令行工具的能力。但“紙上得來終覺淺,絕知此事要躬行”。
在下篇文章中,將帶大家一起用
argparse
實現日常工作中常見的
git
命令,想想是不是有些興奮呢?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
