本教程以循序渐进的方式介绍 argparse,它是 Python 标准库中推荐使用的命令行参数解析模块。
标准库还有两个直接处理命令行参数的模块:较底层的
optparse,以及更底层的getopt。为具体应用配置optparse可能需要更多代码,但它也允许应用请求argparse不支持的某些行为;getopt则对应 C 程序员熟悉的getopt函数族。本指南不直接讲解这两个模块,不过argparse的许多核心概念最早来自optparse,因此部分内容也适合optparse用户。
基本概念
先通过 ls 命令,看看本入门教程将要探索的功能:
$ ls
cpython devguide prog.py pypy rm-unused-function.patch
$ ls pypy
ctypes_configure demo dotviewer include lib_pypy lib-python ...
$ ls -l
total 20
drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
drwxr-xr-x 4 wena wena 4096 Feb 8 12:04 devguide
-rwxr-xr-x 1 wena wena 535 Feb 19 00:05 prog.py
drwxr-xr-x 14 wena wena 4096 Feb 7 00:59 pypy
-rw-r--r-- 1 wena wena 741 Feb 18 01:01 rm-unused-function.patch
$ ls --help
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
...
从这四条命令,可以理解几个概念:
- 即使完全不带选项,
ls也有用:它默认显示当前目录的内容。 - 如果默认行为不能满足需要,就再提供一些信息。这里希望查看另一个目录
pypy,因此指定了一个位置参数。之所以这样命名,是因为程序可以根据值在命令行中的位置判断其用途。cp更能说明这一点:最基本的用法是cp SRC DEST,第一个位置表示要复制什么,第二个位置表示复制到哪里。 - 还可以改变程序的行为。例如,希望显示每个文件的更多信息,而不仅是文件名。这时使用的
-l称为选项参数。 - 最后一部分是帮助文本片段。即使从未用过某个程序,也常能通过阅读帮助文本了解其用法。
基础用法
从一个非常简单、几乎不做任何事情的示例开始:
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
原教程给出的运行示例如下:
$ python prog.py
$ python prog.py --help
usage: prog.py [-h]
options:
-h, --help show this help message and exit
$ python prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo
这里发生了什么?
- 不带选项运行脚本时,标准输出没有任何内容,因此目前还没什么实际用途。
- 第二次调用已经显示了
argparse的价值:几乎没有额外工作,就获得了清楚的帮助信息。 --help(可缩写为-h)是自动获得的唯一选项,无需手动定义。指定其他内容会报错,不过程序仍然会自动给出有用的用法提示。
引入位置参数
来看一个示例:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)
原教程的运行示例如下:
$ python prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python prog.py --help
usage: prog.py [-h] echo
positional arguments:
echo
options:
-h, --help show this help message and exit
$ python prog.py foo
foo
这里发生了什么?
- 加入了
ArgumentParser.add_argument()方法,用它声明程序接受哪些命令行参数。这里将参数命名为echo,与其回显输入的用途相对应。 - 现在调用程序时,必须给出这个位置参数。
ArgumentParser.parse_args()会返回解析后的参数数据,这里包含echo。argparse自动把值保存为返回对象的属性,无需另行指定变量名。属性名也与传给add_argument()的字符串echo相同。
帮助信息虽然整齐,却还不够有用。例如,它表明 echo 是位置参数,但没有解释作用;用户只能猜测或阅读源码。因此,给它补充说明:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args.echo)
帮助输出变为:
$ python prog.py -h
usage: prog.py [-h] echo
positional arguments:
echo echo the string you use here
options:
-h, --help show this help message and exit
接下来,让程序做点更有用的事情:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)
原教程给出的运行示例如下:
$ python prog.py 4
Traceback (most recent call last):
File "prog.py", line 5, in <module>
print(args.square**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
结果并不理想。除非明确指定转换方式,argparse 会把传入的参数值当成字符串。下面告诉它将输入转换成整数:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number",
type=int)
args = parser.parse_args()
print(args.square**2)
原教程给出的运行示例如下:
$ python prog.py 4
16
$ python prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'
这次符合预期。输入不能转换为整数时,程序还会在继续计算之前给出错误并退出。
引入选项参数
前面使用的是位置参数。接下来看看如何添加可选的选项参数:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
print("verbosity turned on")
原教程给出的输出如下:
$ python prog.py --verbosity 1
verbosity turned on
$ python prog.py
$ python prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]
options:
-h, --help show this help message and exit
--verbosity VERBOSITY
increase output verbosity
$ python prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument
这里发生了什么?
- 指定
--verbosity时,程序输出一条信息;不指定时,则不输出。 - 不提供这个选项也不会报错,说明它确实可选。默认情况下,未使用某个选项时,对应属性(这里是
args.verbosity)的值为None,所以不能通过if的真值判断。 - 帮助信息也随之发生了变化。
- 一旦使用
--verbosity,就必须同时提供一个值,具体值在当前示例中不受限制。
上面的示例允许为 --verbosity 传入不同的值,不过这个简单程序实际上只需要开和关,即 True 或 False。据此调整代码:原文将前一示例的值称为“任意整数”,但该代码没有设置 type=int,实际接收的是字符串。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
原教程给出的输出如下:
$ python prog.py --verbose
verbosity turned on
$ python prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python prog.py --help
usage: prog.py [-h] [--verbose]
options:
-h, --help show this help message and exit
--verbose increase output verbosity
这里发生了什么?
- 这个选项现在是一面标志,无需再附带值;名称也改为与此用途对应的
--verbose。新加入的关键字参数action被设为"store_true":出现该选项时,args.verbose为True;没有出现时,为False。 - 再给这个标志附带值会报错,符合无需参数值的标志用法。
- 注意帮助信息的变化。
短选项
如果熟悉命令行,你可能已经注意到,前面还没有介绍选项的短写形式。添加起来很简单:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
原教程给出的运行示例如下:
$ python prog.py -v
verbosity turned on
$ python prog.py --help
usage: prog.py [-h] [-v]
options:
-h, --help show this help message and exit
-v, --verbose increase output verbosity
帮助信息也显示了新加入的短选项。
组合位置参数与选项参数
让程序进一步组合位置参数和选项参数:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
print(f"the square of {args.square} equals {answer}")
else:
print(answer)
原教程给出的输出如下:
$ python prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python prog.py 4
16
$ python prog.py 4 --verbose
the square of 4 equals 16
$ python prog.py --verbose 4
the square of 4 equals 16
- 这里重新引入了必需的位置参数,因此缺少它时会报错。
- 在这个示例中,位置参数与选项的先后顺序不影响结果。
现在恢复多个详细程度级别,并让不同级别真正影响输出:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
原教程给出的输出如下:
$ python prog.py 4
16
$ python prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python prog.py 4 -v 1
4^2 == 16
$ python prog.py 4 -v 2
the square of 4 equals 16
$ python prog.py 4 -v 3
16
除了最后一次调用,前面的输出都符合预期。最后一项暴露了程序的问题,可以通过限制 --verbosity 所接受的值来处理:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
原教程给出的输出如下:
$ python prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square
positional arguments:
square display a square of a given number
options:
-h, --help show this help message and exit
-v, --verbosity {0,1,2}
increase output verbosity
这一限制同时体现在错误信息和帮助文本中。
接下来采用另一种常见的详细程度控制方式,也就是 CPython 可执行程序处理其详细输出参数的方式,可以查看 python --help:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity == 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
这里引入了另一种操作 "count",用来统计指定选项出现的次数。
$ python prog.py 4
16
$ python prog.py 4 -v
4^2 == 16
$ python prog.py 4 -vv
the square of 4 equals 16
$ python prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python prog.py 4 -h
usage: prog.py [-h] [-v] square
positional arguments:
square display a square of a given number
options:
-h, --help show this help message and exit
-v, --verbosity increase output verbosity
$ python prog.py 4 -vvv
16
- 这个选项又变成了标志,与前面
action="store_true"的版本类似,因此再给它传入额外值会报错。 - 单次出现时,它的使用方式也与
store_true类似。 - 示例展示了
count的作用:可以重复选项来提高详细程度。这类写法可能已经很熟悉。 - 如果不指定
-v,对应属性的值仍然是None。 - 使用选项的长写形式,也应得到相同效果。
- 当前帮助信息没有充分说明重复标志的新用法。可以改进脚本说明,例如调整
help参数。 - 最后一个输出再次暴露了程序的问题。
先修复这个问题:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
# bugfix: replace == with >=
if args.verbosity >= 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
原教程给出的结果如下:
$ python prog.py 4 -vvv
the square of 4 equals 16
$ python prog.py 4 -vvvv
the square of 4 equals 16
$ python prog.py 4
Traceback (most recent call last):
File "prog.py", line 11, in <module>
if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
- 第一项输出正确,解决了先前的问题:次数大于或等于 2 时,都应使用最详细的输出。
- 第三项输出仍然不正确。
继续修复这个错误:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
print(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.square}^2 == {answer}")
else:
print(answer)
这里又引入了关键字参数 default,将默认值设为 0,使其能与其他整数比较。记住,选项未指定时默认得到 None;None 不能与整数做这里的大小比较,因此前一版本会抛出 TypeError。
原教程给出的结果如下:
$ python prog.py 4
16
目前学到的内容已经能解决不少问题,但仍只是入门。argparse 的功能很丰富,下面再探索几项。
进一步使用
如果想让这个小程序计算其他次幂,而不只是平方,可以这样扩展:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print(f"{args.x} to the power {args.y} equals {answer}")
elif args.verbosity >= 1:
print(f"{args.x}^{args.y} == {answer}")
else:
print(answer)
原教程给出的输出如下:
$ python prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python prog.py -h
usage: prog.py [-h] [-v] x y
positional arguments:
x the base
y the exponent
options:
-h, --help show this help message and exit
-v, --verbosity
$ python prog.py 4 2 -v
4^2 == 16
到目前为止,详细程度主要用于改变显示的文本。下面则使用它来显示更多文本:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
answer = args.x**args.y
if args.verbosity >= 2:
print(f"Running '{__file__}'")
if args.verbosity >= 1:
print(f"{args.x}^{args.y} == ", end="")
print(answer)
原教程给出的输出如下:
$ python prog.py 4 2
16
$ python prog.py 4 2 -v
4^2 == 16
$ python prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16
明确有歧义的参数
无法确定某个值应被解释为位置参数还是选项时,可以用 -- 告诉 parse_args():后面的内容都按位置参数处理。
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-n', nargs='+')
>>> parser.add_argument('args', nargs='*')
>>> # ambiguous, so parse_args assumes it's an option
>>> parser.parse_args(['-f'])
usage: PROG [-h] [-n N [N ...]] [args ...]
PROG: error: unrecognized arguments: -f
>>> parser.parse_args(['--', '-f'])
Namespace(args=['-f'], n=None)
>>> # ambiguous, so the -n option greedily accepts arguments
>>> parser.parse_args(['-n', '1', '2', '3'])
Namespace(args=[], n=['1', '2', '3'])
>>> parser.parse_args(['-n', '1', '--', '2', '3'])
Namespace(args=['2', '3'], n=['1'])
互斥选项
前面使用了 ArgumentParser 实例的两个方法。现在引入第三个:add_mutually_exclusive_group(),它可以声明互相冲突的选项。为了说明这个功能,也调整程序其余部分,加入与 --verbose 相反的 --quiet。
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{args.x}^{args.y} == {answer}")
为方便演示,程序变得更简单,也去掉了一些前面的功能。原教程给出的输出如下:
$ python prog.py 4 2
4^2 == 16
$ python prog.py 4 2 -q
16
$ python prog.py 4 2 -v
4 to the power 2 equals 16
$ python prog.py 4 2 -vq
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
$ python prog.py 4 2 -v --quiet
usage: prog.py [-h] [-v | -q] x y
prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
这些结果应该容易理解。最后一次调用说明了用法的灵活性:长选项与短选项可以混合使用。
结束前,还可以告诉用户程序的主要用途,以免他们不清楚:
import argparse
parser = argparse.ArgumentParser(description="calculate X to the power of Y")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("x", type=int, help="the base")
parser.add_argument("y", type=int, help="the exponent")
args = parser.parse_args()
answer = args.x**args.y
if args.quiet:
print(answer)
elif args.verbose:
print(f"{args.x} to the power {args.y} equals {answer}")
else:
print(f"{args.x}^{args.y} == {answer}")
注意用法文本中的 [-v | -q]:它表示可以使用 -v 或 -q,但不能同时使用。
$ python prog.py --help
usage: prog.py [-h] [-v | -q] x y
calculate X to the power of Y
positional arguments:
x the base
y the exponent
options:
-h, --help show this help message and exit
-v, --verbose
-q, --quiet
翻译 argparse 输出
argparse 的帮助文本、错误信息等输出,都通过 gettext 支持翻译,应用可以据此将这些消息本地化。也可以参考 国际化教程。
例如,下面这段 argparse 输出:
$ python prog.py --help
usage: prog.py [-h] [-v | -q] x y
calculate X to the power of Y
positional arguments:
x the base
y the exponent
options:
-h, --help show this help message and exit
-v, --verbose
-q, --quiet
其中 usage:、positional arguments:、options: 和 show this help message and exit 都可以翻译。
要翻译这些字符串,需要先提取到 .po 文件。例如,使用 Babel 执行以下命令:
$ pybabel extract -o messages.po /usr/lib/python3.12/argparse.py
这条命令会提取 argparse 模块中可翻译的字符串,并写入 messages.po。它假设 Python 安装在 /usr/lib 下;示例中的 python3.12 路径也应根据实际安装调整。
可以用以下脚本查看当前系统上 argparse 模块的位置:
import argparse
print(argparse.__file__)
翻译 .po 文件中的消息,再通过 gettext 安装翻译后,argparse 就能显示翻译后的消息。
要翻译自己在 argparse 输出中提供的字符串,也使用 gettext。
自定义类型转换器
argparse 允许为命令行参数指定自定义类型转换器。这样可以在用户输入保存到 argparse.Namespace 之前转换它,适合在程序使用输入前进行预处理。
自定义类型转换器可以是任何接受一个字符串参数(即参数值)、并返回转换结果的可调用对象。如果要处理更复杂的场景,则可以通过 action 参数使用自定义操作类。
例如,希望按不同的选项前缀区别处理参数:
import argparse
parser = argparse.ArgumentParser(prefix_chars='-+')
parser.add_argument('-a', metavar='<value>', action='append',
type=lambda x: ('-', x))
parser.add_argument('+a', metavar='<value>', action='append',
type=lambda x: ('+', x))
args = parser.parse_args()
print(args)
原教程给出的输出如下:
$ python prog.py -a value1 +a value2
Namespace(a=[('-', 'value1'), ('+', 'value2')])
这个示例做了两件事:
- 通过
prefix_chars参数创建了使用自定义选项前缀字符的解析器。 - 定义了
-a和+a两个选项,并通过type提供自定义转换器,把前缀与参数值一起保存为元组。
如果不使用这些转换器,两个选项都会把未附带前缀标记的值保存到默认属性 a,无法从值本身区分它们来自 -a 还是 +a。使用转换器后,元组保留了来源前缀,便能区分这两种输入。
结语
argparse 的能力远不止本教程介绍的内容。它的文档详尽,并提供了大量示例。学完本教程后,再阅读这些文档会更容易理解。











暂无评论内容