argparse教程

作者

茨和邦-莱肯霍布

本教程旨在对 argparse ,python标准库中推荐的命令行分析模块。

注解

还有另外两个模块可以完成相同的任务,即 getopt (相当于 getopt() 来自c语言)和不推荐使用的 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]

options:
  -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 ,是我们免费获得的唯一选项(即无需指定)。指定任何其他内容都会导致错误。但即便如此,我们还是免费得到了一条有用的用法信息。

引入位置参数

一个例子:

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

options:
  -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

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)

以下是运行代码的结果:

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

options:
  -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]

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

以下是正在发生的事情:

  • 选项现在更像是一个标志,而不是需要值的东西。我们甚至更改了选项的名称来匹配这个想法。注意,我们现在指定一个新的关键字, action 给它一个价值 "store_true" . 这意味着,如果指定了选项,则指定 Trueargs.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]

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)

现在输出:

$ 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
  • 我们又提出了一个立场参数,因此引起了不满。

  • 注意,顺序无关紧要。

我们给我们的这个程序返回具有多个冗长值的能力,并实际使用它们,怎么样:

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)

输出:

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

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

options:
  -h, --help       show this help message and exit
  -v, --verbosity  increase output verbosity
$ python3 prog.py 4 -vvv
16
  • 是的,它现在更像是一面旗帜(类似于 action="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(f"the square of {args.square} equals {answer}")
elif args.verbosity >= 1:
    print(f"{args.square}^2 == {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的值都尽可能详细。

  • 第三输出不太好。

我们来解决这个问题:

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 以便与其他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(f"{args.x} to the power {args.y} equals {answer}")
elif args.verbosity >= 1:
    print(f"{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

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

注意到目前为止我们一直在使用冗长级别 改变 显示的文本。下面的示例使用详细级别来显示 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(f"Running '{__file__}'")
if args.verbosity >= 1:
    print(f"{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(f"{args.x} to the power {args.y} equals {answer}")
else:
    print(f"{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

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

结论

这个 argparse 模块提供了比这里显示的更多的功能。它的文档非常详细和全面,并且有很多例子。通过本教程后,您应该可以轻松地消化它们,而不会感到不知所措。