Abstract
在命令窗口运行 Python 程序时,有时需要传入一些参数,就用到了 argparse
模块,它有非常强的解析命令行参数的能力。本文对该包进行简要介绍:
- 原始的命令行参数获取方式
sys.argv
; argparse
的基本用法;- 背后的运作机制;
shell
编程;- 更好的方式
configure.py
;
sys.argv
sys.argv
是一个列表,包含了在启动 Python 脚本时传递给解释器的命令行参数。第一个元素是脚本的名称(或路径),之后的元素是按顺序列出的命令行参数:
import sysprint(sys.argv)
print(f'Script name: {sys.argv[0]}') # 脚本的名称
print(f'Argument 1: {sys.argv[1]}') # 第一个命令行参数
print(f'Argument 2: {sys.argv[2]}') # 第二个命令行参数
在命令行中输入:
python .\argparse_demo.py arg1 arg2
输出:
['.\\argparse_demo.py', 'arg1', 'arg2']
Script name: .\argparse_demo.py
Argument 1: arg1
Argument 2: arg2
其实就是 ‘python’ 命令后的内容。需要注意的是,sys.argv
中的命令行参数都是字符串类型。
argparse
先从一个简单的例子开始:
import argparseparser = argparse.ArgumentParser(prog='ProgramName',description='What the program does',epilog='Text at the bottom of help'
)
parser.add_argument('filename') # positional argument
parser.add_argument('-c', '--count') # option that takes avalue
parser.add_argument('-v', '--verbose', action='store_true') # on/off flagargs = parser.parse_args()
print(args)
命令行输入:python prog.py filename.txt -c 3 -v
,得到:
Namespace(filename='filename.txt', count='3', verbose=True)
命令行输入:python prog.py -h
,得到帮助信息:
usage: ProgramName [-h] [-c COUNT] [-v] filename # prog='ProgramName'What the program does # descriptionpositional arguments: # 位置参数filenameoptional arguments: # 可选参数-h, --help show this help message and exit-c COUNT, --count COUNT-v, --verboseText at the bottom of help # epilog
首先要说明的是两类参数,positional arguments 和 optional arguments,这类似于函数签名中的位置参数和可选参数。此处:
- 不以
'-'
开头的普通变量是位置参数,其顺序由其添加顺序决定;- 以
'-'
或'--'
开头的是可选参数,变量名由--***
决定,如此例中的count
和verbose
,若没有--***
,则由-*
决定;后面将进行更详细的说明。
这段例子干了三件事:
- 创建参数解析器
parser = argparse.ArgumentParser(...)
; - 用函数
parser.add_argument(...)
添加了三个参数filename
、count
、verbose
args = parser.parse_args()
解析参数。
现在要了解两个问题:
argparse.ArgumentParser
是什么?- 如何添加和解析参数:
parser.add_argument
、args = parser.parse_args()
?
argparse.ArgumentParser
是什么?
打印解析器对象,print(parser)
输出的内容是:
ArgumentParser(prog='ProgramName',usage=None,description='What the program does',formatter_class=<class 'argparse.HelpFormatter'>,conflict_handler='error',add_help=True
)
可以看到这个对象现有六个参数,一眼就能看懂的有两个:
prog='ProgramName'
是程序脚本文件名,点到源码处,发现是这样的:
# default setting for prog
if prog is None:prog = _os.path.basename(_sys.argv[0])
程序名是可以设置的,不填的话,就默认为 _os.path.basename(_sys.argv[0])
[上面有用]!但我们填了 prog='ProgramName'
。
description
字符串给出了程序的简要描述。
然后可以从 python prog.py -h
的输出中,得知:
usage
:自定义用法说明。如果不指定,则会自动生成一个用法说明,如
usage: ProgramName [-h] [-c COUNT] [-v] filename
虽然实际上这么输入命令是不可行的。看看我们自己设置 usage 会发生什么:
parser = argparse.ArgumentParser(usage='hahaha!!!')
再输出 help 信息,得到的 usage 为:
usage: hahaha!!!
add_help
:当add_help=True
时,帮助选项会被添加到解析器中。可以通过在命令行中使用-h
或--help
来触发该选项,并显示帮助文档。这就是为什么会多出一个可选参数-h, --help
来!如果改为False
:
parser = argparse.ArgumentParser(add_help=False)
再命令行输入 python prog.py --help
,则会报错,说明没有参数 --help
。
-h, --help
比较特别,给出了它,就会输出程序的帮助信息,包括:usage、description、positional arguments 和 optional arguments,而且不会执行脚本程序内容。
剩下的两个要求助 GPT:
formatter_class
:指定帮助文档输出的格式。常用的取值有argparse.RawTextHelpFormatter
(保留原始文本格式)和argparse.ArgumentDefaultsHelpFormatter
(包含参数的默认值)等。【主要是 description 和 epilog 换不换行吧!】conflict_handler
用于指定在出现冲突的情况下如何处理。具体来说,它用于解决当多个参数具有相同的选项字符串时产生的冲突。接受以下值:
~~~~ – ‘error’:默认值。当出现冲突时,抛出一个ArgumentConflictError
异常。
~~~~ – ‘resolve’:自动解决冲突,默认选择后面的参数。这意味着后面出现的参数将覆盖先前出现的参数。
~~~~ – ‘ignore’:忽略冲突,保留先前出现的参数,忽略后面出现的参数。
下面是使用conflict_handler
参数的示例:
import argparseparser = argparse.ArgumentParser(conflict_handler='resolve')
parser.add_argument('-a', '--option', help='Option A')
parser.add_argument('-b', '--option', help='Option B') # -a-b 具有相同的选项字符串args = parser.parse_args()
-a, --option
以及 -b, --option
都具有相同的选项字符串,即存在冲突。由于设置了 conflict_handler='resolve'
,后面出现的参数会覆盖先前出现的参数。因此,如果在命令行中提供了 -avalue1 -b value2
,则 args.option
的值将是 'value2'
。
小结:以上就是对 argparse.ArgumentParser
对象参数的解释,没有特别有用的地方。
如何添加参数:parser.add_argument
?
def add_argument(self,*name_or_flags: str,action: str | Type[Action] = ...,nargs: int | str | _SUPPRESS_T | None = None,const: Any = ...,default: Any = ...,type: (str) -> _T | FileType = ...,choices: Iterable[_T] | None = ...,required: bool = ...,help: str | None = ...,metavar: str | tuple[str, ...] | None = ...,dest: str | None = ...,version: str = ...,**kwargs: Any
) -> Action
名称 | 描述 | 值 |
---|---|---|
action | 指明应当如何处理一个参数 | |
nargs | 参数可被使用的次数 |
|
const | 存储一个常量值 | |
default | 当未提供某个参数时要使用的默认值 | 默认为 |
type | 自动将参数转换为给定的类型 |
|
choices | 将值限制为指定的可选项集合 | |
required | 指明某个参数是必需的还是可选的 |
|
help | 某个参数的帮助消息 | |
metavar | 要在帮助中显示的参数替代显示名称 | |
dest | 指定要在结果命名空间中使用的属性名称 |
函数 parser.add_argument(...)
向解析器中添加命令行参数,并通过上表中的设置告诉解析器如何解析该参数,如 type=int
告诉解析器应当把该参数解析为 int
整数。下面逐一解释这些设置:
*name_or_flags
设置参数名。根据 Python 的语法,这个 *name_or_flags
带 *
,是可变位置参数,可接收多个实参,也意味着可以给添加的参数取多个名字?来试试:
parser.add_argument('filename', 'name')
命令行 python prog.py file.txt
,报错:
ValueError: invalid option string 'filename': must start with a character '-'
无效的 option,即,多于一个的普通参数名,会被当作可选参数,且提示你应以 '-'
开头。
是的,上面的可选参数 '-c', '--count'
和 '-v', '--verbose'
,可以有多个名,可以有不止两个哦:
parser.add_argument('-c', '--count', '--cc', '---ddd')
可正常运行,且这四个名称都可用,不过最终的变量名由第一个横线数大于等于 2 的字符串决定,比如此处:args.count
。
action
& const
& default
指明应当如何处理一个可选参数,注意是可选参数,因为位置参数的灵活性很低,仅仅是简单地将出现在命令行中的非 -
字符串按顺序依次赋给位置参数,而可选参数要列出参数名,把要赋予的值跟在其后。
默认是 action='store'
,将跟在参数名后的字符串赋予该参数,如:
python prog.py filename.txt -c 3
将 3 赋给了 args.count
。这是最常见的,实际上函数的这三个参数搭配使用相当灵活。下面以 -c, --count
介绍常见的用法:
action='store'
时。将跟在-c
后的 3 赋给args.count
;如果可选参数-c, --count
未列出到命令行,则会将default=***
[默认default=None] 的值赋给args.count
;action='store_const'
时。-c
后不必跟随任何值,跟了也会被赋给位置参数。args.count=const
if 命令行中出现-c
elseargs.count=default
;【const=***
必须设置;此时不能再设置type
,因为 const 已经确定了】action='store_true'
时。args.verbose=True
if 命令行中出现-v
elseargs.verbose=False
;【等价于上面的const=True, default=False
】
简单来说,action='store_const'
时,const 和 default 二选一,action='store_true'
时,True 和 False 二选一。
append
系列很少用,区别在于args.count
会被当作列表:
python prog.py filename.txt -c 3 # args.count=[3]
python prog.py filename.txt -c 3 -c 2 # args.count=[3, 2]
append_const
就类比吧;count
就是计数:
parser.add_argument('-c', '--count', action='count', default=5)
python prog.py file.txt # args.count=5
python prog.py file.txt -c -c -c # args.count=3
nargs
控制 -c
接收几个参数:
nargs=int>0
:接收int
个参数
parser.add_argument('-c', '--count', nargs=1)
python prog.py file.txt -c 1 # args.count=['1']
# 结果是 list
parser.add_argument('-c', '--count', nargs=3)
python prog.py file.txt -c 1 2 3 # args.count=['1', '2', '3']
nargs='?'
:接收至多一个参数
parser.add_argument('-c', '--count', nargs='?', default=5)
python prog.py file.txt -c # args.count=None, default=5 不起作用
python prog.py file.txt -c 1 # args.count=['1']
nargs='+'
:至少一个;nargs='*'
:任意个。
choices
固定参数可接受的值范围:
parser.add_argument('-c', '--count', type=int, choices=[1, 2, 3]) # 要和 type 一致
python prog.py file.txt -c 2 # args.count=2
python prog.py file.txt -c 4 # error
required
有点鸡肋,可选参数为什么还要搞出这么一个参数?大概是想实行 key=value
的参数供应形式,又不让 default
吧!
dest
& metavar
前面说参数名是 args.count
,那只是缺省这两个参数的情况。当:
parser.add_argument('-c', '--count', dest='true_name', metavar='help_name')
命令行输入 python prog.py file.txt --count 2
,得到【注意 true_name
】:
Namespace(filename='file.txt', true_name='2', verbose=False)
命令行输入 python prog.py -h
,得到【注意 help_name
】:
optional arguments:-h, --help show this help message and exit-c help_name, --count help_name-v, --verbose
真正的参数名是由 dest=***
决定的,而 metavar
则是显示在帮助信息中。
如何解析参数?
函数 parser.add_argument(...)
的源码内容相当复杂,但我们知道 parser.parse_args()
返回了一个 Namespace
对象:
args = parser.parse_args() = Namespace(filename='file.txt', true_name='2', verbose=False)
包含了所有参数。然后还可以看到很多:
setattr(self, name, kwargs[name])
setattr(namespace, self.dest, not option_string.startswith('--no-'))
setattr(namespace, self.dest, values) # action='store'
setattr(namespace, self.dest, self.const) # action='store_const'
setattr(namespace, self.dest, items)
setattr(namespace, self.dest, items)
...
清一色的为 namespace
对象设置属性 self.dest
,就是刚才说的决定参数名的。上面各行应该就是对应不同的 action
。
其他
一个官方示例:可以给参数赋值任何对象
以下代码是一个 Python 程序,它获取一个整数列表并计算总和或者最大值:
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))
当使用适当的参数运行时,它会输出命令行传入整数的总和或者最大值:
python prog.py 1 2 3 4
4
python prog.py 1 2 3 4 --sum
10
函数 sum
和 max
也可作为参数的赋值。
ArgumentParser 中的参数 allow_abbrev
在不引起混淆的情况下,命令行参数允许缩写。
假如:
parser.add_argument('-opt')
那么命令行中写:
# 需要是 `opt` 的前缀
python prog.py -o 4
python prog.py -op 4
python prog.py -opt 4
都是一样的,得到变量名都是 args.opt
。
假如:
parser.add_argument('--option')
那么命令行中写:
python prog.py --o 4
python prog.py --op 4
python prog.py --opt 4
python prog.py --option 4
都是一样的,得到变量名都是 args.option
。
同时提供了 '-o', '--option'
的话,则最终的变量名为 'args.option'
,即双横杠的为变量名其他用法都一样。
parser.add_argument('-opt', '--option')
命令行中则只需写其前缀 -o, -op, -opt, ...
,当然,-o
最简单。
【注】关于变量名,add_argument
中的参数 dest
才是最牛的,只要指定 dest='***'
,那最终的变量名就是 args.***
,不论 '-o', '--option'
。