python argparse:命令行參數解析詳解

簡介

本文介紹的是argparse模塊的基本使用方法,尤其詳細介紹add_argument內建方法各個參數的使用及其效果。

本文翻譯自argparse的官方說明,並加上一些筆者的理解

import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=max,
                    help='sum the integers (default: find the max)')

args = parser.parse_args()
print(args.accumulate(args.integers))

如上示例,argparse模塊的基本使用包含5個步驟:

  1. import模塊:import argparse
  2. 獲解析器對象:argparse.ArgumentParser
  3. 利用解析器對象內建方法,添加參數解析規則:add_argument
  4. 利用解析器對象內建方法,開始解析參數,獲取解析結果對象:parse_args
  5. 從解析結果對象中,獲取參數值:parse_args

解析器類(ArgumentParser)

在第2步中,我們通過ArgumentParser()函數的調用獲取瞭解析器對象ArgumentParser

在瞭解解析器對象的各個成員之前,我們先對一段正常的說明文本進行區間劃分

# usage字段
usage: 程序名 [-h|--help] .....

# Description字段
程序功能描述

# 位置參數說明(必選)
positional arguments:
 ...
 
# 可選參數說明
optional arguments:
...

# 補充說明字段
...

例如

usage: PROG [-h] [--foo [FOO]] bar [bar ...]

bar help

positional arguments:
 bar          bar help

optional arguments:
 -h, --help   show this help message and exit
 --foo [FOO]  foo help
 
 And that's how you'd foo a bar

關於位置參數與可選參數的理解,參考下一章節:添加參數解析規則

在上述的區間劃的認識下,我們再來看看解析器對象的成員及其功能

名字 默認值 功能
prog sys.argv[0] -h時顯示的程序名
usage - usage字段描述
description None description字段描述
epilog None 補充字段描述
parents None 從父(公共)解析器中繼承所有的參數選項
formatter_class None 定製說明文本的顯示風格
prefix_class - 定製前綴字符,例如前綴"-b"改爲“+b"
add_help True 是否使能顯示參數 -h --help
allow_abbrev True 是否支持長參數
fromfile_prefix_chars None
argument_default None
conflict_handler None

比較常用的是description,例如:

parser = argparse.ArgumentParser(description='Process some integers.')

或者

parser = argparse.ArgumentParser()
parser.descritpioin="Process some integers."

添加參數解析規則(add_argument)

add_argument是解析器類ArgumentParser的內建方法,用於向解析器添加參數解析規則

ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type]
        [, choices][, required][, help][, metavar][, dest])

內建方法支持以下的關鍵字,我們會對每一個關鍵字及其效果做進一步說明

關鍵字 簡介
name or flags 參數名或者"-/--"開頭的選項,例如foo或者-f, --foo
action 匹配到選項後的行爲
nargs 選項跟隨的參數個數
const 在某些actionnargs下,使用的固定值
default 默認值
type 參數類型
choices 可選的參數值範圍
required 選項必選or可選
help 參數描述
metavar 使用說明中顯示的參數名
dest 選項最終在解析結果對象中的名字

關鍵字name or flags

關鍵字name是什麼?flags又是什麼?兩者有什麼差別?

name表示參數名,其賦值與位置順序相關,因此也叫位置參數名,命令行中必須賦值

flags表示-|--開頭的參數名,命令行中可選參數

例如:

#可選的flags參數
parser.add_argument('-f', '--foo')
#必選的name位置參數
parser.add_argument('bar0')
parser.add_argument('bar1')

這裏假設--foo需要帶1個參數,那麼

prog arg1 --foo arg2 arg3
  • arg1 是位置參數bar0的值
  • arg2 是可選參數'-f, --foo'的值
  • arg3 是位置參數bar1的值

換句話來說,在輸入的命令行中,除去所有-|--開頭的參數及其帶上的參數值之外,剩下的參數都爲位置參數,其順序依次對應使用add_argument註冊的位置參數順序。

在命令行調用中,位置參數必須賦值,即每個必選參數都要有賦值;而可選參數(-|--)可根據需求選擇

關鍵字action

功能如其名,關鍵字action控制匹配到命令行選項後的行爲。參數解析後不是保存值就好了?我們繼續看看。

關鍵字action只支持以下值,我們稍後再詳細講解每一個值的含義:

含義
store 保存參數值
store_const 關鍵字const配合使用,保存關鍵字const的值
store_true 保存值爲True
store_false 保存值爲False
append 保存多次選項值爲列表
append_const 關鍵字const配合使用,保存關鍵字const的值爲列表
const 保存選項的出現次數
help 效果等效於-h--help
version 打印版本

store

保存參數值,這也是默認的行爲,我們看個例子:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo')
>>> parser.parse_args('--foo 1'.split())
Namespace(foo='1')

parse_args()之後,--foo選項的值就保存到了parse_args()的解析結果對象中。

我們可以這樣獲取解析結果對象的值:

args = parser.parse_args('--foo 1'.split())
print(args.foo)

總結來說,選項名成爲了解析結果對象的成員,而選項對應的值則成了成員的值

到這裏,我們存在2個疑惑:

  1. 當同時支持-f--foo時,生成的成員名是什麼呢?能自定義名字麼?
  2. PROG -f這樣不需要帶參數的選項,存儲的成員值是什麼樣的呢?

不用急,我們繼續往下看

store_const

在匹配到選項後,存儲關鍵字const的值,常用於命令行中不帶參數的選項。例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='store_const', const=42)
>>> parser.parse_args(['--foo'])
Namespace(foo=42)

從上面的例子,我們可以看到,在匹配到--foo後,對應成員foo的保存爲了const參數的值。

但更多時候,我們不帶參數的選項更多隻是表示布爾型的開關,這時候我們可用store_true或者store_false

store_true 和 store_false

store_truestore_false是一種特殊的store_const,存儲的值類型爲布爾型TrueFalse。例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='store_true')
>>> parser.add_argument('--bar', action='store_false')
>>> parser.add_argument('--baz', action='store_false')
>>> parser.parse_args('--foo --bar'.split())
Namespace(foo=True, bar=False, baz=True)

示例中有3個布爾型“開關”,可以發現有以下特點:

  1. store_true在匹配到命令選項後,保存爲True;相對的,store_false保存爲False
  2. 當命令行中沒匹配到開關後,例如示例中的baz,則保存爲store_false的相反值True

append

把所有值保存爲一個列表,常用於支持多次選項的情況,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='append')
>>> parser.parse_args('--foo 1 --foo 2'.split())
Namespace(foo=['1', '2'])

append_const

store_const非常相似,只是把值存儲爲一個列表。此時如果沒有指定關鍵字const,則默認爲Noneappend_const常用於多個不同選項值需要存儲到相同成員列表的情況。例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--str', dest='types', action='append_const', const=str)
>>> parser.add_argument('--int', dest='types', action='append_const', const=int)
>>> parser.parse_args('--str --int'.split())
Namespace(types=[<class 'str'>, <class 'int'>])

理解上文需要知道以下前提:

  1. 關鍵字dest用於指定成員名
  2. 參數的值可以是各種對象,包括類型對象,即示例中的str類型int類型

在示例中,--str的常量值是str類型,以列表形式保存到types成員--int的常量值是int類型,以列表形式保存到types成員

count

如果我們只需要統計選項出現的次數,此處可以用count,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--verbose', '-v', action='count')
>>> parser.parse_args(['-vvv'])
Namespace(verbose=3)

help

打印幫助信息,功能等效於-h|--help

version

打印版本信息,需要配合關鍵字version使用,例如:

>>> import argparse
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--version', action='version', version='%(prog)s 2.0')
>>> parser.parse_args(['--version'])
PROG 2.0

關鍵字nargs

關鍵字nargsnumber argumemts的縮寫,表示選項有多少個參數,其支持以下值:

含義
N (an integer) 收集N個參數到列表
'?' 無參數或單個參數
'*' 大於等於0個參數
'+' 大於等於1個參數
argparse.REMAINDER 只收集不解析

當沒有指定關鍵字nargs時,其實際的值取決於關鍵字action,例如當action = "store"時,默認獲取1個參數,當action = "store_true"時,選項不需要帶參數。

N (an integer)

此處的N,表示一個整型數字,含義爲選項需要提供N個參數。例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', nargs=2)
>>> parser.add_argument('bar', nargs=1)
>>> parser.parse_args('c --foo a b'.split())
Namespace(bar=['c'], foo=['a', 'b'])

需要注意的是,當nargs = 1時,其並不等效於關鍵字nargs的默認情況。例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('bar0', nargs=1)
>>> parser.add_argument('bar1')
>>> parser.parse_args('a b'.split())
Namespace(bar0=['a'], bar1='b')

可以發現,nargs = 1時,例如bar0,其值是一個列表,一個只有1個元素的列表;而默認情況下,就是一個元素,官方稱爲item

'?'

nargs='?'可以實現3種場景:

  1. 輸入的命令行中,選項帶參數時,值爲附帶的參數
  2. 輸入的命令行中,沒有改選項時,值爲關鍵字default的值
  3. 輸入的命令行中,有選項但沒帶參數時,值爲關鍵字const的值(只適用於可選選項(flag))

例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', nargs='?', const='c', default='d')
>>> parser.add_argument('bar', nargs='?', default='d')
>>> parser.parse_args(['XX', '--foo', 'YY'])
Namespace(bar='XX', foo='YY')
>>> parser.parse_args(['XX', '--foo'])
Namespace(bar='XX', foo='c')
>>> parser.parse_args([])
Namespace(bar='d', foo='d')

一個更常用的場景,是實現可選的輸入輸出文件,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
...                     default=sys.stdin)
>>> parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'),
...                     default=sys.stdout)
>>> parser.parse_args(['input.txt', 'output.txt'])
Namespace(infile=<_io.TextIOWrapper name='input.txt' encoding='UTF-8'>,
          outfile=<_io.TextIOWrapper name='output.txt' encoding='UTF-8'>)
>>> parser.parse_args([])
Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>,
          outfile=<_io.TextIOWrapper name='<stdout>' encoding='UTF-8'>)

上述例子中,如果命令行沒有指定input,則使用標準輸入stdin;如果命令行沒有指定output,則使用標準輸出stdout

'*'

nargs=2會限制一定要有2個參數,如果需要任意多個參數呢?我們可以用nargs='*',例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', nargs='*')
>>> parser.add_argument('--bar', nargs='*')
>>> parser.add_argument('baz', nargs='*')
>>> parser.parse_args('a b --foo x y --bar 1 2'.split())
Namespace(bar=['1', '2'], baz=['a', 'b'], foo=['x', 'y'])

nargs=N相似,最終的值是列表類型。

'+'

nargs='+'nargs='*'從功能上非常相似,唯一不同的地方在於,nargs='+'要求至少要有1個參數,否則會報錯。

'?''+''*'的定義與正則表達式中的+*非常相似

在正則表達式中,

  • ?:表示0或1個字符
  • +:表示大於等於1個字符
  • *:表示大於等於0個字符

我們看個例子:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('foo', nargs='+')
>>> parser.parse_args(['a', 'b'])
Namespace(foo=['a', 'b'])
>>> parser.parse_args([])
usage: PROG [-h] foo [foo ...]
PROG: error: the following arguments are required: foo

argparse.REMAINDER

nargs=argparse.REMAINDER常用於收集參數後傳遞給其他的命令行解析工具,其不會解析-|--,只是收集所有選項到列表。

例如:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--foo')
>>> parser.add_argument('command')
>>> parser.add_argument('args', nargs=argparse.REMAINDER)
>>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split()))
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')

上例中,argparse沒有解析args選項的--arg1,而是全部收集到了一個列表

關鍵字const

關鍵字acton關鍵字nargs中,其實已經涉及了關鍵字const的所有功能。

關鍵字const只是存儲一個常量值,在以下兩種情況下才會使用:

  1. action='store_const'或者action='append_const'
  2. nargs='?'

當使用action='store_const'action='append_const'時,關鍵字const必須提供,對其他的action關鍵字時,默認值爲None

關鍵字default

有時候,選項不帶參數,或者命令行沒對應選項,這時候就可以使用默認值,而關鍵字default存儲的就是默認值。默認情況下,關鍵字default的值爲None。例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', default=42)
>>> parser.parse_args(['--foo', '2'])
Namespace(foo='2')
>>> parser.parse_args([])
Namespace(foo=42)

如果關鍵字default賦值的是字符串,而關鍵字type有指定參數類型,那麼就會把字符串轉爲關鍵字type指定的類型,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--length', default='10', type=int)
>>> parser.add_argument('--width', default=10.5, type=int)
>>> parser.parse_args()
Namespace(length=10, width=10.5)

如果關鍵字nargs?或者*,那麼default的值會在命令行沒有參數時使用,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('foo', nargs='?', default=42)
>>> parser.parse_args(['a'])
Namespace(foo='a')
>>> parser.parse_args([])
Namespace(foo=42)

關鍵字default也提供一種特殊用法:default=argparse.SUPPRESS。在這種情況下,如果命令行並沒有匹配的選項,那麼並不會在解析結果對象中添加選項對應的成員,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', default=argparse.SUPPRESS)
>>> parser.parse_args([])
Namespace()
>>> parser.parse_args(['--foo', '1'])
Namespace(foo='1')

關鍵字type

默認情況下,argparse解析的參數默認爲字符串類型,當然也可以通過關鍵字type指定其他任何類型,例如floatint,甚至是文件類型file, 例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('foo', type=int)
>>> parser.add_argument('bar', type=open)
>>> parser.parse_args('2 temp.txt'.split())
Namespace(bar=<_io.TextIOWrapper name='temp.txt' encoding='UTF-8'>, foo=2)

如果關鍵字type指定的是文件類型,我們還可以通過```FileType('w')以可寫形式打開文件,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('bar', type=argparse.FileType('w'))
>>> parser.parse_args(['out.txt'])
Namespace(bar=<_io.TextIOWrapper name='out.txt' encoding='UTF-8'>)

關鍵字type甚至能指定爲函數,經過函數處理後的返回值作爲參數值,例如:

>>> def perfect_square(string):
...     value = int(string)
...     sqrt = math.sqrt(value)
...     if sqrt != int(sqrt):
...         msg = "%r is not a perfect square" % string
...         raise argparse.ArgumentTypeError(msg)
...     return value
...
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('foo', type=perfect_square)
>>> parser.parse_args(['9'])
Namespace(foo=9)
>>> parser.parse_args(['7'])
usage: PROG [-h] foo
PROG: error: argument foo: '7' is not a perfect square

關鍵字choices

當我們需要限制選項的值範圍,我們可以用關鍵字choices關鍵字choices限定了參數值的可選列表,如果命令行提供的參數值不在列表中,則會報錯,例如:

>>> parser = argparse.ArgumentParser(prog='game.py')
>>> parser.add_argument('move', choices=['rock', 'paper', 'scissors'])
>>> parser.parse_args(['rock'])
Namespace(move='rock')
>>> parser.parse_args(['fire'])
usage: game.py [-h] {rock,paper,scissors}
game.py: error: argument move: invalid choice: 'fire' (choose from 'rock',
'paper', 'scissors')

當然,需要注意的是,關鍵字choice的值必須符合關鍵字type指定的類型。

關鍵字required

默認情況下,-f--foo都是可選的,但如果需要改爲必選,可以使用關鍵字required,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', required=True)
>>> parser.parse_args(['--foo', 'BAR'])
Namespace(foo='BAR')
>>> parser.parse_args([])
usage: argparse.py [-h] [--foo FOO]
argparse.py: error: option --foo is required

關鍵字help

關鍵字help是選項的說明,在-h或者--help時會顯示出來,例如:

>>> parser = argparse.ArgumentParser(prog='frobble')
>>> parser.add_argument('--foo', action='store_true',
...                     help='foo the bars before frobbling')
>>> parser.add_argument('bar', nargs='+',
...                     help='one of the bars to be frobbled')
>>> parser.parse_args(['-h'])
usage: frobble [-h] [--foo] bar [bar ...]

positional arguments:
 bar     one of the bars to be frobbled

optional arguments:
 -h, --help  show this help message and exit
 --foo   foo the bars before frobbling

當然,關鍵字help也支持格式化顯示,%(prog)s和大部分add_argument()的關鍵字,包括%(default)s%(type)s,等等,例如:

>>> parser = argparse.ArgumentParser(prog='frobble')
>>> parser.add_argument('bar', nargs='?', type=int, default=42,
...                     help='the bar to %(prog)s (default: %(default)s)')
>>> parser.print_help()
usage: frobble [-h] [bar]

positional arguments:
bar     the bar to frobble (default: 42)

optional arguments:
-h, --help  show this help message and exit

格式爲%(keyword)s,如果需要顯示%,就需要使用%%

還存在一種特殊情況,假設我們不希望參數顯示在--help中,我們可以用:argparse.SUPPRESS,例如:

>>> parser = argparse.ArgumentParser(prog='frobble')
>>> parser.add_argument('--foo', help=argparse.SUPPRESS)
>>> parser.print_help()
usage: frobble [-h]

optional arguments:
 -h, --help  show this help message and exit

關鍵字dest

argparse會把解析的結果保存成解析結果對象的屬性,但是,屬性名是什麼呢?例如,parser.add_argument(’-f', '--foo'),解析結果是保存在屬性f中還是foo中?關鍵字dest就是用於定製屬性名的。

我們先了解默認情況下,屬性名是什麼?

對位置參數而言,關鍵字dest默認爲第一個參數名,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('bar')
>>> parser.parse_args(['XXX'])
Namespace(bar='XXX')

對可選參數而言,關鍵字dest首選第一個出現的長參數名。如果沒有長參數,則選擇第一個短參數名。不管選擇的是長參數還是短參數,都會把-|---給去掉,同時把名字中的-符號替換爲_,以符合python的變量命名規則,例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-f', '--foo-bar', '--foo')
>>> parser.add_argument('-x', '-y')
>>> parser.parse_args('-f 1 -x 2'.split())
Namespace(foo_bar='1', x='2')
>>> parser.parse_args('--foo 1 -y 2'.split())
Namespace(foo_bar='1', x='2')

我們再看看,如果定製屬性名有什麼效果?

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', dest='bar')
>>> parser.parse_args('--foo XXX'.split())
Namespace(bar='XXX')

關鍵字metavar

在執行-h|--help,顯示的幫助信息中,如何定製選項帶的參數名呢?例如:

-t T    loop times

我希望修改顯示的TTIMES,更直觀。這時候我們就可以使用關鍵字metavar了。

在默認情況下,對位置參數,則會直接顯示參數名,對可選參數,則會轉爲大寫。例如:

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo')
>>> parser.add_argument('bar')
>>> parser.parse_args('X --foo Y'.split())
Namespace(bar='X', foo='Y')
>>> parser.print_help()
usage:  [-h] [--foo FOO] bar

positional arguments:
 bar

optional arguments:
 -h, --help  show this help message and exit
 --foo FOO

上例中,位置參數bar直接顯示爲bar,而可選參數--foo帶的參數名就轉大寫顯示FOO

如果我們定製顯示的參數名,該怎麼做呢?

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', metavar='YYY')
>>> parser.add_argument('bar', metavar='XXX')
>>> parser.parse_args('X --foo Y'.split())
Namespace(bar='X', foo='Y')
>>> parser.print_help()
usage:  [-h] [--foo YYY] XXX

positional arguments:
XXX

optional arguments:
-h, --help  show this help message and exit
--foo YYY

當然,還存在一種特殊情況,就是有多個參數nargs=N,這時候關鍵字metavar可以以列表形式提供啦,例如:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-x', nargs=2)
>>> parser.add_argument('--foo', nargs=2, metavar=('bar', 'baz'))
>>> parser.print_help()
usage: PROG [-h] [-x X X] [--foo bar baz]

optional arguments:
-h, --help     show this help message and exit
-x X X
--foo bar baz

最後,有一點要注意的時,關鍵字metavar關鍵字dest不一樣的地方在於,關鍵字metavar僅僅只影響-h|--help的顯示效果,關鍵dest則同時影響解析結果屬性名

獲取解析結果

action='store'中提到:選項名成爲了解析結果對象的成員,而選項對應的值則成了成員的值,所以如果我們需要獲取解析後的結果,直接使用解析結果的成員值就好了。例如:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-x', dest='xyz')
>>> parser.add_argument('--foo', nargs=2, metavar=('bar', 'baz'))
>>> args = parser.parse_args("--foo a b -x c".split())
>>> print(args.foo)
>>> ['a', 'b']
>>> print(args.xyz)
>>> c
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章