Skip to main content

Argparse教程

author:

Tshepang Lekhonkhobe

本教程旨在温柔地介绍 argparse,这是Python标准库中推荐的命令行解析模块。

注解

还有两个完成相同任务的其他模块,即 getopt (与C语言相对应的 getopt())和已弃用的 optparse。还要注意,argparse 是基于 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()

以下是运行代码的结果:

$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h]

optional arguments:
  -h, --help  show this help message and exit
$ python3 prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python3 prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo

这里是发生了什么:

  • 运行不带任何选项的脚本不会向stdout显示任何内容。没有那么有用。

  • 第二个开始显示 argparse 模块的有用性。我们几乎没有做什么,但是我们已经得到了一个很好的帮助消息。

  • --help 选项,也可以缩短为 -h,是我们免费获得的唯一选项(即无需指定它)。指定任何其他值会导致错误。但即使如此,我们也得到了一个有用的使用消息,也是免费的。

介绍Positional参数

一个例子:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)

并运行代码:

$ python3 prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python3 prog.py --help
usage: prog.py [-h] echo

positional arguments:
  echo

optional arguments:
  -h, --help  show this help message and exit
$ python3 prog.py foo
foo

这里是发生了什么:

  • 我们添加了 add_argument() 方法,这是我们用来指定程序愿意接受哪些命令行选项。在这种情况下,我将其命名为 echo,以使其符合其功能。

  • 现在调用我们的程序需要我们指定一个选项。

  • parse_args() 方法实际上从指定的选项(在本例中为 echo)返回一些数据。

  • 变量是 argparse 自由执行的某种形式的“魔术”(即不需要指定该值存储在哪个变量中)。你还会注意到它的名字匹配给方法的字符串参数,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)

我们得到:

$ python3 prog.py -h
usage: prog.py [-h] echo

positional arguments:
  echo        echo the string you use here

optional arguments:
  -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)

以下是运行代码的结果:

$ python3 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 将我们给出的选项视为字符串,除非我们另有说明。所以,让我们告诉 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)

以下是运行代码的结果:

$ python3 prog.py 4
16
$ python3 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")

而输出:

$ python3 prog.py --verbosity 1
verbosity turned on
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]

optional arguments:
  -h, --help            show this help message and exit
  --verbosity VERBOSITY
                        increase output verbosity
$ python3 prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument

这里是发生了什么:

  • 程序被编写为在指定 --verbosity 时显示某事,否则不显示。

  • 要显示该选项实际上是可选的,运行没有它的程序没有错误。注意,默认情况下,如果不使用可选参数,则相关变量(在本例中为 args.verbosity)将被赋予 None 作为值,这是 if 语句的真值测试失败的原因。

  • 帮助消息有点不同。

  • 当使用 --verbosity 选项时,还必须指定一些值,任何值。

上面的例子接受 --verbosity 的任意整数值,但对于我们的简单程序,只有两个值实际上有用,TrueFalse。让我们相应地修改代码:

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")

而输出:

$ python3 prog.py --verbose
verbosity turned on
$ python3 prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python3 prog.py --help
usage: prog.py [-h] [--verbose]

optional arguments:
  -h, --help  show this help message and exit
  --verbose   increase output verbosity

这里是发生了什么:

  • 该选项现在更多的是一个标志比需要一个值。我们甚至更改了选项的名称以匹配该想法。注意,我们现在指定一个新的关键字 action,并赋值为 "store_true"。这意味着,如果指定了选项,则将值 True 分配给 args.verbose。不指定它意味着 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")

这里:

$ python3 prog.py -v
verbosity turned on
$ python3 prog.py --help
usage: prog.py [-h] [-v]

optional arguments:
  -h, --help     show this help message and exit
  -v, --verbose  increase output verbosity

请注意,新功能也反映在帮助文本中。

组合Positional和Optional参数

我们的计划越来越复杂:

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("the square of {} equals {}".format(args.square, answer))
else:
    print(answer)

现在的输出:

$ python3 prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python3 prog.py 4
16
$ python3 prog.py 4 --verbose
the square of 4 equals 16
$ python3 prog.py --verbose 4
the square of 4 equals 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,
                    help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
    print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

而输出:

$ python3 prog.py 4
16
$ python3 prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python3 prog.py 4 -v 1
4^2 == 16
$ python3 prog.py 4 -v 2
the square of 4 equals 16
$ python3 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("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

而输出:

$ python3 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)
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square

positional arguments:
  square                display a square of a given number

optional arguments:
  -h, --help            show this help message and exit
  -v {0,1,2}, --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("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

我们引入了另一个动作“count”来计算特定可选参数的出现次数:

$ python3 prog.py 4
16
$ python3 prog.py 4 -v
4^2 == 16
$ python3 prog.py 4 -vv
the square of 4 equals 16
$ python3 prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python3 prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v] square

positional arguments:
  square           display a square of a given number

optional arguments:
  -h, --help       show this help message and exit
  -v, --verbosity  increase output verbosity
$ python3 prog.py 4 -vvv
16
  • 是的,它现在更像是一个标志(类似于 action="store_true")在我们的脚本的以前的版本。这应该解释投诉。

  • 它的行为类似于“store_true”操作。

  • 现在这里是一个“计数”行动给出的示范。你可能以前看过这种用法。

  • 如果不指定 -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("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

这是它给了:

$ python3 prog.py 4 -vvv
the square of 4 equals 16
$ python3 prog.py 4 -vvvv
the square of 4 equals 16
$ python3 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尽可能详细。

  • 第三次输出不太好。

让我们修复这个bug:

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("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

我们刚刚推出了另一个关键字 default。我们将它设置为 0,以使其可与其他int值相比。记住,默认情况下,如果没有指定可选参数,它将获得 None 值,并且不能与int值(因此是 TypeError 异常)进行比较。

和:

$ python3 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("{} to the power {} equals {}".format(args.x, args.y, answer))
elif args.verbosity >= 1:
    print("{}^{} == {}".format(args.x, args.y, answer))
else:
    print(answer)

输出:

$ python3 prog.py
usage: prog.py [-h] [-v] x y
prog.py: error: the following arguments are required: x, y
$ python3 prog.py -h
usage: prog.py [-h] [-v] x y

positional arguments:
  x                the base
  y                the exponent

optional arguments:
  -h, --help       show this help message and exit
  -v, --verbosity
$ python3 prog.py 4 2 -v
4^2 == 16

注意,到目前为止,我们一直使用详细级别到 change 显示的文本。以下示例改为使用详细程度级别来显示 more 文本:

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("Running '{}'".format(__file__))
if args.verbosity >= 1:
    print("{}^{} == ".format(args.x, args.y), end="")
print(answer)

输出:

$ python3 prog.py 4 2
16
$ python3 prog.py 4 2 -v
4^2 == 16
$ python3 prog.py 4 2 -vv
Running 'prog.py'
4^2 == 16

冲突的选择

到目前为止,我们一直在使用 argparse.ArgumentParser 实例的两种方法。让我们介绍第三个,add_mutually_exclusive_group()。它允许我们指定彼此冲突的选项。让我们改变程序的其余部分,使新的功能更有意义:我们将介绍 --quiet 选项,这将是 --verbose 的一个反面:

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("{} to the power {} equals {}".format(args.x, args.y, answer))
else:
    print("{}^{} == {}".format(args.x, args.y, answer))

我们的程序现在更简单,为了演示我们失去了一些功能。无论如何,这里是输出:

$ python3 prog.py 4 2
4^2 == 16
$ python3 prog.py 4 2 -q
16
$ python3 prog.py 4 2 -v
4 to the power 2 equals 16
$ python3 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
$ python3 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("{} to the power {} equals {}".format(args.x, args.y, answer))
else:
    print("{}^{} == {}".format(args.x, args.y, answer))

请注意,使用文本中的细微差异。注意 [-v | -q],它告诉我们,我们可以使用 -v-q,但不能同时两个:

$ python3 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

optional arguments:
  -h, --help     show this help message and exit
  -v, --verbose
  -q, --quiet

结论

argparse 模块提供了比这里显示的更多。它的文档是相当详细和彻底,充满了例子。经过这个教程,你应该轻松地消化它们,而不感到不知所措。