Python是一門(mén)非常好用的腳本語(yǔ)言着绊,自然使用它來(lái)開(kāi)發(fā)命令行程序也比較方便。而且Python的標(biāo)準(zhǔn)庫(kù)中有一個(gè)名為argparse的庫(kù)刨沦,可以非常方便的讓我們把命令行參數(shù)轉(zhuǎn)換成所需的數(shù)據(jù)格式仿便。下面就讓我們來(lái)看看如何使用argparse標(biāo)準(zhǔn)庫(kù)吧。
如果想詳細(xì)了解argparse的話慷丽,可以查看Python官方文檔,目前已經(jīng)出了中文版文檔鳄哭,極大的方便了我們中國(guó)開(kāi)發(fā)者要糊,雖然有些地方?jīng)]有完整翻譯,但是已經(jīng)完全夠用了妆丘。
初見(jiàn)argparse
首先來(lái)看看argparse的基本用法锄俄,下面是最簡(jiǎn)單的一個(gè)例子∩准穑可以看到argparse標(biāo)準(zhǔn)庫(kù)其實(shí)用起來(lái)很簡(jiǎn)單奶赠,分成三個(gè)步驟:
- 創(chuàng)建
ArgumentParser
對(duì)象 - 使用
add_argument
方法添加參數(shù) - 使用
parse_args
方法接受并解析對(duì)象
下面我們就依次來(lái)看看這些步驟吧。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-from', type=str)
parser.add_argument('-to', type=str, default='everyone')
args = parser.parse_args('-from yitian'.split(' '))
print(args)
# 運(yùn)行結(jié)果
# Namespace(from='yitian', to='everyone')
ArgumentParser
ArgumentParser
是最重要的一個(gè)類(lèi)药有,我們要使用argparse標(biāo)準(zhǔn)庫(kù)就必然需要?jiǎng)?chuàng)建這個(gè)類(lèi)的實(shí)例毅戈。如果有需要的話苹丸,可以通過(guò)在構(gòu)造函數(shù)中設(shè)置各種參數(shù)的方式來(lái)修改ArgumentParser
類(lèi)的行為。由于參數(shù)項(xiàng)比較多苇经,所以官方文檔建議我們使用關(guān)鍵字參數(shù)的方式來(lái)添加參數(shù)赘理。
-
prog - 程序的名稱(默認(rèn):
sys.argv[0]
),默認(rèn)是Python程序的文件名 - usage - 描述程序用途的字符串(默認(rèn)值:從添加到解析器的參數(shù)生成)
- description - 在參數(shù)幫助文檔之前顯示的文本(默認(rèn)值:無(wú))
- epilog - 在參數(shù)幫助文檔之后顯示的文本(默認(rèn)值:無(wú))
-
parents - 一個(gè)
ArgumentParser
對(duì)象的列表扇单,它們的參數(shù)也應(yīng)包含在內(nèi)商模。假如多個(gè)parser有一些可以共享的參數(shù),可以通過(guò)設(shè)置子parser的方式來(lái)共享 - formatter_class - 用于自定義幫助文檔輸出格式的類(lèi)
- prefix_chars - 可選參數(shù)的前綴字符集合(默認(rèn)值:'-')
-
fromfile_prefix_chars - 當(dāng)需要從文件中讀取其他參數(shù)時(shí)蜘澜,用于標(biāo)識(shí)文件名的前綴字符集合(默認(rèn)值:
None
) -
argument_default - 參數(shù)的全局默認(rèn)值(默認(rèn)值:
None
)阻桅,假如需要給所有參數(shù)指定一個(gè)相通的默認(rèn)值,可以修改這個(gè)兼都,如果希望全局禁用默認(rèn)值,可以使用argparse.SUPRESS
- conflict_handler - 解決沖突選項(xiàng)的策略(通常是不必要的)
-
add_help - 為解析器添加一個(gè)
-h/--help
選項(xiàng)(默認(rèn)值:True
) -
allow_abbrev - 如果縮寫(xiě)是無(wú)歧義的稽寒,則允許縮寫(xiě)長(zhǎng)選項(xiàng) (默認(rèn)值:
True
)
add_argument方法
有了Parser實(shí)例扮碧,就可以調(diào)用它的add_argument
方法來(lái)添加程序可以接受的參數(shù)了。這個(gè)參數(shù)比較復(fù)雜杏糙,功能也十分強(qiáng)大慎王。
-
name or flags - 一個(gè)命名或者一個(gè)選項(xiàng)字符串的列表,例如
foo
或-f, --foo
宏侍。 - action - 當(dāng)參數(shù)在命令行中出現(xiàn)時(shí)使用的動(dòng)作基本類(lèi)型赖淤。
- nargs - 命令行參數(shù)應(yīng)當(dāng)消耗的數(shù)目。
- const - 被一些 action 和 nargs 選擇所需求的常數(shù)谅河。
- default - 當(dāng)參數(shù)未在命令行中出現(xiàn)時(shí)使用的值咱旱。
- type - 命令行參數(shù)應(yīng)當(dāng)被轉(zhuǎn)換成的類(lèi)型。
- choices - 可用的參數(shù)的容器绷耍。
- required - 此命令行選項(xiàng)是否可省略 (僅選項(xiàng)可用)吐限。
- help - 一個(gè)此選項(xiàng)作用的簡(jiǎn)單描述。
- metavar - 在使用方法消息中使用的參數(shù)值示例褂始。
-
dest - 被添加到
parse_args()
所返回對(duì)象上的屬性名诸典。
參數(shù)名
方法的第一個(gè)參數(shù)是參數(shù)名稱,可以是一個(gè)字符串(name)或者是-
開(kāi)頭的一組字符串(flags)崎苗,前者是位置參數(shù)狐粱,會(huì)按照添加的順序被讀取胆数;后者是關(guān)鍵字參數(shù)肌蜻,可以以任意順序指定。如果指定的是關(guān)鍵字參數(shù)(flags)幅慌,可以同時(shí)添加縮寫(xiě)和完整名宋欺,它們分別需要用-
和--
來(lái)做前綴。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('foo')
parser.add_argument('bar')
parser.add_argument('-f', '--ff', action='store_true')
args = parser.parse_args('FOO BAR -f'.split(' '))
print(args)
args = parser.parse_args('--ff BAR FOO'.split(' '))
print(args)
'''運(yùn)行結(jié)果,注意指定的參數(shù)和獲取到的結(jié)果的不同
Namespace(bar='BAR', ff=True, foo='FOO')
Namespace(bar='FOO', ff=True, foo='BAR')
'''
action(行為)
接下來(lái)介紹的參數(shù)都是關(guān)鍵字參數(shù)齿诞,可以按需制定酸休。這里比較重要的一個(gè)是action,它指定了讀取參數(shù)接下來(lái)要做什么祷杈。這部分可以看官方文檔斑司,介紹的比較全面,而且有對(duì)應(yīng)的例子但汞。我這里只簡(jiǎn)單介紹一下宿刮。
- store和store_const用來(lái)保存具體的值,他們之間有一些區(qū)別私蕾,一會(huì)再介紹僵缺。
- store_true和store_false比較方便,可以用來(lái)設(shè)置一些開(kāi)關(guān)參數(shù)踩叭。例如我想指定
-f
的時(shí)候開(kāi)啟某個(gè)功能磕潮,忽略這個(gè)參數(shù)的時(shí)候不執(zhí)行,就可以把它的行為指定成store_true容贝,然后在程序中就可以得到f名字的參數(shù)真值自脯,然后簡(jiǎn)單的條件判斷就可以了。 - append和append_const會(huì)將參數(shù)存儲(chǔ)成一個(gè)列表斤富。
- count會(huì)存儲(chǔ)參數(shù)出現(xiàn)的次數(shù)膏潮。常見(jiàn)用法是指定日志輸出級(jí)別,例如有的程序
-v
會(huì)顯示簡(jiǎn)單輸出满力,-vv
會(huì)顯示復(fù)雜輸出焕参。
甚至如果需求更復(fù)雜的話,還可以自己實(shí)現(xiàn)一個(gè)新的Action類(lèi)油额,然后添加給add_argument
方法龟糕。
nargs(參數(shù)數(shù)目)
這個(gè)參數(shù)指定你的程序可以接受的參數(shù)個(gè)數(shù),可以使用以下幾個(gè)值:
- N(一個(gè)正整數(shù))悔耘,表示后面的N個(gè)值會(huì)被讀取為參數(shù)讲岁,注意指定為1的話會(huì)變成一個(gè)單元素列表。
-
?
衬以,和正則表達(dá)式里的概念差不多缓艳,后面的一個(gè)值會(huì)被讀取,如果沒(méi)有的話會(huì)從default讀入看峻。 -
+
阶淘,后面的多個(gè)值會(huì)被讀取,如果沒(méi)有會(huì)拋出異常互妓。 -
?
溪窒,后面的多個(gè)值會(huì)被讀取坤塞,沒(méi)有值的話也可以。 -
argparse.REMAINDER
澈蚌,它會(huì)將后面所有值讀取為一個(gè)參數(shù)摹芙,通常用作向其他命令行傳遞參數(shù)用。
默認(rèn)情況下nargs會(huì)按照action的類(lèi)型來(lái)判斷參數(shù)個(gè)數(shù)宛瞄,store和store_const會(huì)讀取后面的一個(gè)值作為參數(shù)浮禾。
const
這個(gè)參數(shù)需要和帶有const的action來(lái)配合使用。
default
指定參數(shù)的默認(rèn)值份汗。這里有一個(gè)很有趣的點(diǎn)盈电,如果你看官方文檔比較仔細(xì)的話,可能會(huì)產(chǎn)生一個(gè)和我一樣的疑問(wèn):store_const和const配合使用可以指定默認(rèn)值杯活,而store和default也可以指定默認(rèn)值匆帚,那么它們之間有什么區(qū)別呢?其實(shí)區(qū)別還是蠻大的旁钧,看看下面這個(gè)例子就明白了卷扮。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-foo', action='store', nargs='?', default=10, const=20)
print(parser.parse_args('-foo 0'.split(' ')))
print(parser.parse_args('-foo'.split(' ')))
print(parser.parse_args(''))
'''
Namespace(foo='0')
Namespace(foo=20)
Namespace(foo=10)
'''
- 當(dāng)foo參數(shù)完全忽略的時(shí)候,會(huì)使用default的值均践。
- 當(dāng)指定了foo參數(shù),但沒(méi)有指定后面的值時(shí)摩幔,會(huì)使用const的值彤委。
- 當(dāng)指定了foo參數(shù)和后面的值時(shí),會(huì)使用我們指定的值或衡。
type
這個(gè)是指定參數(shù)類(lèi)型的焦影,int、float封断、str那些斯辰。當(dāng)然比較特殊的一個(gè)是open,它會(huì)將參數(shù)作為文件來(lái)打開(kāi)坡疼。
如果默認(rèn)的open還不能滿足彬呻,還可以使用argparse.FileType
,它提供了讀寫(xiě)模式柄瑰、文件編碼闸氮、緩沖區(qū)大小等詳細(xì)設(shè)置。
parser.add_argument('bar', type=argparse.FileType('w'))
甚至有需求的話教沾,這里還可以使用我們自己的函數(shù)蒲跨,只要它的參數(shù)是一個(gè)字符串,返回值是轉(zhuǎn)換以后的值就可以授翻。
choices
如果確認(rèn)參數(shù)范圍限定是幾個(gè)定值或悲,可以使用choices參數(shù)來(lái)指定孙咪,可接受的值包括字面值列表以及range
函數(shù)。
required
指定參數(shù)是否是必須的巡语。
metavar和dest
metavar參數(shù)用來(lái)指定參數(shù)的顯示名稱翎蹈,而dest用來(lái)指定參數(shù)底層使用的屬性名。
注意下面的程序輸出捌臊,foo參數(shù)只修改了metavar杨蛋,所以在幫助信息輸出中發(fā)生了變化,但是在Namespace底層仍然使用foo保存值理澎。而bar參數(shù)修改了dest逞力,所以底層屬性名發(fā)生了變化,但是在幫助信息中并沒(méi)有什么改變糠爬。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-foo', metavar='foooo')
parser.add_argument('-bar', dest='barrrr')
parser.print_help()
print(parser.parse_args(''))
'''
usage: argparse_sample.py [-h] [-foo foooo] [-bar bar]
optional arguments:
-h, --help show this help message and exit
-foo foooo
-bar bar
Namespace(barrrr=None, foo=None)
'''
help
有條件的話最好給每個(gè)參數(shù)添加幫助信息寇荧,這樣使用者在用-h
命令的時(shí)候就可以看到參數(shù)的幫助信息了。
parse_args方法
編輯好了參數(shù)执隧,就可以調(diào)用parse_args
方法來(lái)處理參數(shù)了揩抡,它會(huì)返回一個(gè)命名空間對(duì)象,包含了解析之后的參數(shù)镀琉。如果要測(cè)試方法的話峦嗤,可以手動(dòng)給它傳遞一組參數(shù),否則的話屋摔,它會(huì)自動(dòng)從命令行參數(shù)讀取烁设。另外它還支持幾個(gè)比較有用的特性:
- 如果參數(shù)比較長(zhǎng),可以使用等號(hào)來(lái)連接參數(shù)與值钓试,例如
-foo=bar
装黑。 - 如果參數(shù)是單字母長(zhǎng)度的,可以將參數(shù)和值直接寫(xiě)在一起弓熏。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-foo')
parser.add_argument('-b')
print(parser.parse_args('-foo=bar'.split()))
print(parser.parse_args('-bX'.split()))
'''
Namespace(b=None, foo='bar')
Namespace(b='X', foo=None)
'''
另外還支持默認(rèn)無(wú)歧義的參數(shù)縮寫(xiě)恋谭。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-f1aaaaaaaaaa')
parser.add_argument('-f2aaaaaaaaaa')
print(parser.parse_args('-f1 a -f2 b'.split(' ')))
命名空間對(duì)象
前面也看到了,解析完參數(shù)返回的值是命名空間對(duì)象挽鞠,它用起來(lái)非常簡(jiǎn)單疚颊,直接訪問(wèn)屬性值就可以了。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a')
parser.add_argument('-b')
args = parser.parse_args('-a a -b b'.split(' '))
print(args.a)
# a
其他特性
argparse還支持一些其他特性信认,這里就不多做介紹了串稀,詳情請(qǐng)直接參考官方文檔。
- 子parser狮杨。有些程序支持子命令母截,這時(shí)候可以使用子parser創(chuàng)建更復(fù)雜的parser。
- FileType橄教。更詳細(xì)的設(shè)置文件參數(shù)的方法清寇。
- 參數(shù)組喘漏。如果參數(shù)比較多,可以使用參數(shù)組的方式將功能相近的參數(shù)進(jìn)行分組华烟,這樣不管是開(kāi)發(fā)人員還是使用人員都能更清晰的使用參數(shù)翩迈。
- 互斥參數(shù)組。如果某些參數(shù)不能同時(shí)使用盔夜,可以將它們加到互斥參數(shù)組中负饲。
- 部分解析。默認(rèn)情況下
parse_argument
在遇到未知參數(shù)的時(shí)候會(huì)報(bào)錯(cuò)喂链,如果需要保存這些參數(shù)傳遞給其他命令行的時(shí)候返十,可以使用parse_known_args
方法。它不會(huì)因?yàn)槲粗獏?shù)報(bào)錯(cuò)椭微, 而且會(huì)將所有不認(rèn)識(shí)的參數(shù)存儲(chǔ)成一個(gè)列表作為第二個(gè)返回值洞坑。
簡(jiǎn)單實(shí)例
扯了大半天,大家可能還是有點(diǎn)不會(huì)用argparse蝇率,不過(guò)其實(shí)只要看一個(gè)簡(jiǎn)單的例子就可以了迟杂。保存下面的文件,然后用命令行調(diào)用本慕,看看不同的參數(shù)會(huì)有什么輸出排拷。
import argparse
parser = argparse.ArgumentParser(prog='ParserSample',
description='簡(jiǎn)單實(shí)例程序,學(xué)習(xí)如何解析命令行參數(shù)',
epilog='很簡(jiǎn)單就可以學(xué)會(huì)')
parser.add_argument('greeting', type=str, help='問(wèn)候信息锅尘,必需')
parser.add_argument('-fromm', default='yitian', type=str, help='發(fā)送人监氢,默認(rèn)是易天')
parser.add_argument('-to', default='everyone', type=str, nargs='*', help='接收人,默認(rèn)是所有人')
parser.add_argument('-p', action='store_true', help='是否添加感嘆號(hào)')
args = parser.parse_args()
output = f'{args.fromm} say {args.greeting} to {args.to}'
if args.p:
output = output + '!'
print(output)
'''
usage: ParserSample [-h] [-fromm FROMM] [-to [TO [TO ...]]] [-p] greeting
簡(jiǎn)單實(shí)例程序鉴象,學(xué)習(xí)如何解析命令行參數(shù)
positional arguments:
greeting 問(wèn)候信息,必需
optional arguments:
-h, --help show this help message and exit
-fromm FROMM 發(fā)送人何鸡,默認(rèn)是易天
-to [TO [TO ...]] 接收人纺弊,默認(rèn)是所有人
-p 是否添加感嘆號(hào)
很簡(jiǎn)單就可以學(xué)會(huì)
'''