继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

python argparse 模块

DemonLS
关注TA
已关注
手记 43
粉丝 235
获赞 1326

Python docs

这篇文章主要是对 argparse(Python标准库中推荐的命令行解析模块) 进行简要介绍。

note 还有两个其他模块也可以完成相同的任务,分别是 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 命令最基本的用法是 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

下面是这段代码做了什么的解释:

  • 不带任何参数执行这个脚本没有任何的输出,没有什么作用。

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

  • --help 选项可以简写成 -h,它是唯一一个我们能指定的选项(即不需要代码定义),指定任何其他的都会导致报错。但即使这样,我们仍然已经免费获得了帮助信息。

位置参数介绍

一个例子:

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

运行这段代码:

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

代码解释:

  • 我们增加了 add_argument() 方法,这个方法用于指定程序可以接受的命令行。在这个例子中,我将它命名为 echo,与它的功能相符合。

  • 这时我们调用程序需要我们指定选项。

  • parse_args() 方法实际上会从我们指定的选项中返回一些数据,在这个例子中为 echo。

  • 最近的 args.echo 是 argparse 内部执行的某种“魔法”(不需要手动指定哪个变量来存储值)。同时它的名称与执行程序时传入的字符串是一致的。

然而请注意,尽管帮助信息看起来不错,但目前并不是很有用。例如,我们看到了我们将 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 语句为 False 的原因。

  • 帮助信息有一点不同。

  • 当我们使用 --verbosity 选项时,还必须指定一些值,任意值都可以。

上面的例子对于 --verbosity 还可以接受任意整数,但对我们的程序来说,只有 True 和 False 是有用的。因此我们修改代码如下:

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”。这意味着如果我们这个选项指定了,我们将给 args.verbose 赋值为 True,反之为 False。

  • 当你手动指定选项的值时会报错,因为它实际存储的是 True。

  • 注意帮助信息的不同。

短选项

如果你熟悉命令行的用法,你们发现目前为止我还没有涉及这个选项的简短版本的话题。下面是一个很简单的示例:

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

注意在帮助信息中反映了这个新的功能。

位置参数与可选参数的结合

我们的程序变得越来越复杂了:

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

上面结果除了最后一个看上去都没问题。最后一个暴露了我们程序的一个 bug。让我们通过限制 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

需要注意的是,对应的改变在报错信息和帮助信息中都有体现。

现在,让我们用另外一种更通用的方式来使用 verbosity 选项。这种方式与 CPython 解释器中处理 verbosity 选项相同(可通过 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)

我们引入了另外一种 action:"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_action" 的作用类似。

  • 这里是对 "count" 作用的证明。你可以在之前已经看到过这一个用法。

  • 如果你不指定 -v 标识,这个标识被认为是 None。

  • 正如我们认为的一样,当我们使用长选项,输出结果仍然是一样的。

  • 然而,我们的帮助信息对这一个新功能解释得不是很好,但这一点仍旧是可以通过修改脚本代码来修复的(通过 help 关键字)。

  • 最后一个输出暴露了我们程序的一个 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",
                    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'
  • 开始的输出结果很理想,并且修复了之前的bug。也就是说,我们希望任何 >=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 是为了让它可以进行整数比较。记住,默认情况下,如果一个可选参数没有被指定,它将得到 None 值,它不能进行整数比较(因此会报 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

请注意,到目前为止,我们一直在使用 verbosity 级别来更改显示的文本。下面的示例改为使用 verbosity 级别来显示更多文本:

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] 是告诉我们可以使用任意一个,但不能同时使用:

$ 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模块提供的功能远远超出此处所示。它的文档非常详细和透彻,并有许多示例。阅读完本教程后,您应该轻松消化它们,而不会感到不知所措。

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP