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

note 还有两个其他模块也可以完成相同的任务,分别是 getopt(与C语言中的 getopt() 等效)和已经过时的 optparse。需要注意的是 argparse 也是基于 optparse,因此在用法上非常相似。

概念

让我们通过使用 ls 命令来展示我们将在本Python学习教程入门中探索的功能类型:

$lscpythondevguideprog.pypypyrm-unused-function.patch$lspypyctypes_configuredemodotviewerincludelib_pypylib-python...$ls-ltotal20drwxr-xr-x19wenawena4096Feb1818:51cpythondrwxr-xr-x4wenawena4096Feb812:04devguide-rwxr-xr-x1wenawena535Feb1900:05prog.pydrwxr-xr-x14wenawena4096Feb700:59pypy-rw-r--r--1wenawena741Feb1801:01rm-unused-function.patch$ls--helpUsage:ls[OPTION]...[FILE]...ListinformationabouttheFILEs(thecurrentdirectorybydefault).Sortentriesalphabeticallyifnoneof-cftuvSUXnor--sortisspecified....

从这四个命令中我们可以学到一些概念:

ls 命令在不接受任何参数时也是有作用的,它默认用于展示当前目录下的内容。如果我们想它让提供非默认以外的功能,我们必须指定更多的参数。在这个例子中,我们想要展示一个不同的目录:pypy。我们所做的是指定所谓的位置参数。之所以这样命名,是因为程序仅根据命令行中的位置知道该值的用途。这个概念和 cp 之类的命令更相关,cp 命令最基本的用法是 cp SRC DEST。第一个位置是你要复制的内容,第二个位置是你要复制到的位置。现在,假设我们要更改程序的行为。在我们的示例中,我们为每个文件显示更多偏偏,而不仅仅是显示文件名。在这种情况下,-l被称为可选参数。这是帮助文本的的一小段。它非常有用,因为你可以通过它找到从未使用过的程序,并且只需要阅读帮助文本即可了解其工作方式。

基础用法

让我们从一个非常简单的例子开始,这个例子几乎什么事情都没做:

importargparseparser=argparse.ArgumentParser()parser.parse_args()

下面是执行这段代码的结果:

$python3prog.py$python3prog.py--helpusage:prog.py[-h]optionalarguments:-h,--helpshowthishelpmessageandexit$python3prog.py--verboseusage:prog.py[-h]prog.py:error:unrecognizedarguments:--verbose$python3prog.pyfoousage:prog.py[-h]prog.py:error:unrecognizedarguments:foo

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

不带任何参数执行这个脚本没有任何的输出,没有什么作用。第二个开始显示 argparse 模块的用处,我们几乎什么也没做,但是已经得到了很好的帮助信息--help 选项可以简写成 -h,它是唯一一个我们能指定的选项(即不需要代码定义),指定任何其他的都会导致报错。但即使这样,我们仍然已经免费获得了帮助信息。

位置参数介绍

一个例子:

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

运行这段代码:

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

代码解释:

我们增加了 add_argument() 方法,这个方法用于指定程序可以接受的命令行。在这个例子中,我将它命名为 echo,与它的功能相符合。这时我们调用程序需要我们指定选项。parse_args() 方法实际上会从我们指定的选项中返回一些数据,在这个例子中为 echo。最近的 args.echo 是 argparse 内部执行的某种“魔法”(不需要手动指定哪个变量来存储值)。同时它的名称与执行程序时传入的字符串是一致的。

然而请注意,尽管帮助信息看起来不错,但目前并不是很有用。例如,我们看到了我们将 echo 作为了位置参数,但除了猜测和阅读源代码外,我们不知道它的作用。因此,让我们将它变得更加有用:

importargparseparser=argparse.ArgumentParser()parser.add_argument("echo",help="echothestringyouusehere")args=parser.parse_args()print(args.echo)

这时我们将得到:

$python3prog.py-husage:prog.py[-h]echopositionalarguments:echoechothestringyouusehereoptionalarguments:-h,--helpshowthishelpmessageandexit

现在,让我们来做一些更有用的事情:

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",help="displayasquareofagivennumber")args=parser.parse_args()print(args.square**2)

上面代码执行的结果如下:

$python3prog.py4Traceback(mostrecentcalllast):File"prog.py",line5,in<module>print(args.square**2)TypeError:unsupportedoperandtype(s)for**orpow():'str'and'int'

结果不是很好,这是因为 argparse 将我们给的选项当成了字符串,除非我们手动指定类型。因此,让我们来告诉 argparse 将它视为一个整数:

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",help="displayasquareofagivennumber",type=int)args=parser.parse_args()print(args.square**2)

下面是上述代码的执行结果:

$python3prog.py416$python3prog.pyfourusage:prog.py[-h]squareprog.py:error:argumentsquare:invalidintvalue:'four'

结果很好,程序甚至可以在执行前因为错误输入而结束。

可选参数介绍

到目前为止,我们已经介绍过了位置参数。接下来让我们看下如何添加一个可选参数:

importargparseparser=argparse.ArgumentParser()parser.add_argument("--verbosity",help="increaseoutputverbosity")args=parser.parse_args()ifargs.verbosity:print("verbosityturnedon")

运行结果:

$python3prog.py--verbosity1verbosityturnedon$python3prog.py$python3prog.py--helpusage:prog.py[-h][--verbosityVERBOSITY]optionalarguments:-h,--helpshowthishelpmessageandexit--verbosityVERBOSITYincreaseoutputverbosity$python3prog.py--verbosityusage:prog.py[-h][--verbosityVERBOSITY]prog.py:error:argument--verbosity:expectedoneargument

代码解释如下:

程序用于当指定了 --verbosity 时输出一些信息,而如果没有指定则不展示任何信息。为了证明选项确实是可选的,当我们不带选项运行程序时并没有报错。需要注意的是,如果一个可选参数没有指定,与它相关联的参数(在这个例子中是 args.verbosity),默认情况下会赋值为 None,这也是 if 语句为 False 的原因。帮助信息有一点不同。当我们使用 --verbosity 选项时,还必须指定一些值,任意值都可以。

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

importargparseparser=argparse.ArgumentParser()parser.add_argument("--verbose",help="increaseoutputverbosity",action="store_true")args=parser.parse_args()ifargs.verbose:print("verbosityturnedon")

结果如下:

python3prog.py--verboseverbosityturnedon$python3prog.py--verbose1usage:prog.py[-h][--verbose]prog.py:error:unrecognizedarguments:1$python3prog.py--helpusage:prog.py[-h][--verbose]optionalarguments:-h,--helpshowthishelpmessageandexit--verboseincreaseoutputverbosity

代码解释如下:

现在这个选项更像是一个标记,而不是仅仅需要一个值。我们甚至修改了这个选项的名字来匹配这个想法。需要注意的是,我们指定了一个新的关键词:action,并且它的值为:“store_true”。这意味着如果我们这个选项指定了,我们将给 args.verbose 赋值为 True,反之为 False。当你手动指定选项的值时会报错,因为它实际存储的是 True。注意帮助信息的不同。

短选项

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

importargparseparser=argparse.ArgumentParser()parser.add_argument("-v","--verbose",help="increaseoutputverbosity",action="store_true")args=parser.parse_args()ifargs.verbose:print("verbosityturnedon")

执行结果如下:

$python3prog.py-vverbosityturnedon$python3prog.py--helpusage:prog.py[-h][-v]optionalarguments:-h,--helpshowthishelpmessageandexit-v,--verboseincreaseoutputverbosity

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

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

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

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",type=int,help="displayasquareofagivennumber")parser.add_argument("-v","--verbose",action="store_true",help="increaseoutputverbosity")args=parser.parse_args()answer=args.square**2ifargs.verbose:print("thesquareof{}equals{}".format(args.square,answer))else:print(answer)

输出如下:

$python3prog.pyusage:prog.py[-h][-v]squareprog.py:error:thefollowingargumentsarerequired:square$python3prog.py416$python3prog.py4--verbosethesquareof4equals16$python3prog.py--verbose4thesquareof4equals16我们重新加入了位置参数,因此(直接运行程序)会报错注意与选项顺序无关

我们如何让我们这个程序具有多个 verbosity 值,并实际使用这些值:

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",type=int,help="displayasquareofagivennumber")parser.add_argument("-v","--verbosity",type=int,help="increaseoutputverbosity")args=parser.parse_args()answer=args.square**2ifargs.verbosity==2:print("thesquareof{}equals{}".format(args.square,answer))elifargs.verbosity==1:print("{}^2=={}".format(args.square,answer))else:print(answer)

输出如下:

$python3prog.py416$python3prog.py4-vusage:prog.py[-h][-vVERBOSITY]squareprog.py:error:argument-v/--verbosity:expectedoneargument$python3prog.py4-v14^2==16$python3prog.py4-v2thesquareof4equals16$python3prog.py4-v316

上面结果除了最后一个看上去都没问题。最后一个暴露了我们程序的一个 bug。让我们通过限制 verbosity 可接受的值来修复它:

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",type=int,help="displayasquareofagivennumber")parser.add_argument("-v","--verbosity",type=int,choices=[0,1,2],help="increaseoutputverbosity")args=parser.parse_args()answer=args.square**2ifargs.verbosity==2:print("thesquareof{}equals{}".format(args.square,answer))elifargs.verbosity==1:print("{}^2=={}".format(args.square,answer))else:print(answer)

输出如下:

$python3prog.py4-v3usage:prog.py[-h][-v{0,1,2}]squareprog.py:error:argument-v/--verbosity:invalidchoice:3(choosefrom0,1,2)$python3prog.py4-husage:prog.py[-h][-v{0,1,2}]squarepositionalarguments:squaredisplayasquareofagivennumberoptionalarguments:-h,--helpshowthishelpmessageandexit-v{0,1,2},--verbosity{0,1,2}increaseoutputverbosity

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

现在,让我们用另外一种更通用的方式来使用 verbosity 选项。这种方式与 CPython 解释器中处理 verbosity 选项相同(可通过 python --help 查看):

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",type=int,help="displaythesquareofagivennumber")parser.add_argument("-v","--verbosity",action="count",help="increaseoutputverbosity")args=parser.parse_args()answer=args.square**2ifargs.verbosity==2:print("thesquareof{}equals{}".format(args.square,answer))elifargs.verbosity==1:print("{}^2=={}".format(args.square,answer))else:print(answer)

我们引入了另外一种 action:"count",它用于计算一个可选参数出现的次数:

$python3prog.py416$python3prog.py4-v4^2==16$python3prog.py4-vvthesquareof4equals16$python3prog.py4--verbosity--verbositythesquareof4equals16$python3prog.py4-v1usage:prog.py[-h][-v]squareprog.py:error:unrecognizedarguments:1$python3prog.py4-husage:prog.py[-h][-v]squarepositionalarguments:squaredisplayasquareofagivennumberoptionalarguments:-h,--helpshowthishelpmessageandexit-v,--verbosityincreaseoutputverbosity$python3prog.py4-vvv16

是的,现在我们的脚本比之前的版本多了一个标识(类似于之前的 action="store_true")。可以在报错信息中看到这一解释。

它和 "store_action" 的作用类似。

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

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

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

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

最后一个输出暴露了我们程序的一个 bug。

让我们来修复它:

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",type=int,help="displayasquareofagivennumber")parser.add_argument("-v","--verbosity",action="count",help="increaseoutputverbosity")args=parser.parse_args()answer=args.square**2#bugfix:replace==with>=ifargs.verbosity>=2:print("thesquareof{}equals{}".format(args.square,answer))elifargs.verbosity>=1:print("{}^2=={}".format(args.square,answer))else:print(answer)

输出如果如下:

$python3prog.py4-vvvthesquareof4equals16$python3prog.py4-vvvvthesquareof4equals16$python3prog.py4Traceback(mostrecentcalllast):File"prog.py",line11,in<module>ifargs.verbosity>=2:TypeError:'>='notsupportedbetweeninstancesof'NoneType'and'int'

开始的输出结果很理想,并且修复了之前的bug。也就是说,我们希望任何 >=2的值都尽可能详细。

第三个输出结果不是很理想

让我们修复这个 bug:

importargparseparser=argparse.ArgumentParser()parser.add_argument("square",type=int,help="displayasquareofagivennumber")parser.add_argument("-v","--verbosity",action="count",default=0,help="increaseoutputverbosity")args=parser.parse_args()answer=args.square**2ifargs.verbosity>=2:print("thesquareof{}equals{}".format(args.square,answer))elifargs.verbosity>=1:print("{}^2=={}".format(args.square,answer))else:print(answer)

我们刚刚引入了另外一个关键词:default。我们把它设置为 0 是为了让它可以进行整数比较。记住,默认情况下,如果一个可选参数没有被指定,它将得到 None 值,它不能进行整数比较(因此会报 TypeError 异常)。

这时我们再执行:

$python3prog.py416

就目前为止所学到的东西,您可以走的很远,而且我们只是从头开始。 argparse模块功能非常强大,在结束本教程之前,我们将对其进行更多的探索。

深入一点

如果我们想扩展我们的小程序以执行其他幂运算,而不仅仅是做平方:

importargparseparser=argparse.ArgumentParser()parser.add_argument("x",type=int,help="thebase")parser.add_argument("y",type=int,help="theexponent")parser.add_argument("-v","--verbosity",action="count",default=0)args=parser.parse_args()answer=args.x**args.yifargs.verbosity>=2:print("{}tothepower{}equals{}".format(args.x,args.y,answer))elifargs.verbosity>=1:print("{}^{}=={}".format(args.x,args.y,answer))else:print(answer)

输出:

$python3prog.pyusage:prog.py[-h][-v]xyprog.py:error:thefollowingargumentsarerequired:x,y$python3prog.py-husage:prog.py[-h][-v]xypositionalarguments:xthebaseytheexponentoptionalarguments:-h,--helpshowthishelpmessageandexit-v,--verbosity$python3prog.py42-v4^2==16

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

importargparseparser=argparse.ArgumentParser()parser.add_argument("x",type=int,help="thebase")parser.add_argument("y",type=int,help="theexponent")parser.add_argument("-v","--verbosity",action="count",default=0)args=parser.parse_args()answer=args.x**args.yifargs.verbosity>=2:print("Running'{}'".format(__file__))ifargs.verbosity>=1:print("{}^{}==".format(args.x,args.y),end="")print(answer)

结果:

$python3prog.py4216$python3prog.py42-v4^2==16$python3prog.py42-vvRunning'prog.py'4^2==16

冲突的选项

目前为止,我们一直使用 argparse.ArgumentParser 实例的两个方法。让我们引入第三个:add_mutually_exclusive_group()。它允许我们指定相互冲突的选项。让我们修改程序的其他部分,以便让我们引入新功能变得更有意义:我们将引入 --quiet 选项,它是 --verbose 的对立:

importargparseparser=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="thebase")parser.add_argument("y",type=int,help="theexponent")args=parser.parse_args()answer=args.x**args.yifargs.quiet:print(answer)elifargs.verbose:print("{}tothepower{}equals{}".format(args.x,args.y,answer))else:print("{}^{}=={}".format(args.x,args.y,answer))

我们的程序更加简单了,为了演示我们丢掉了一些功能。总之,下面是输出结果:

$python3prog.py424^2==16$python3prog.py42-q16$python3prog.py42-v4tothepower2equals16$python3prog.py42-vqusage:prog.py[-h][-v|-q]xyprog.py:error:argument-q/--quiet:notallowedwithargument-v/--verbose$python3prog.py42-v--quietusage:prog.py[-h][-v|-q]xyprog.py:error:argument-q/--quiet:notallowedwithargument-v/--verbose

那应该很容易理解。在最后一个输出里,我添加了长选项与短选项的混合,这样你能看到选项顺序的灵活性。

在我们总结之前,你可能会想告诉用户你程序主要用来做什么,以防他们不知道:

importargparseparser=argparse.ArgumentParser(description="calculateXtothepowerofY")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="thebase")parser.add_argument("y",type=int,help="theexponent")args=parser.parse_args()answer=args.x**args.yifargs.quiet:print(answer)elifargs.verbose:print("{}tothepower{}equals{}".format(args.x,args.y,answer))else:print("{}^{}=={}".format(args.x,args.y,answer))

需要注意的是帮助信息有些许的变化。注意 [-v | -q] 是告诉我们可以使用任意一个,但不能同时使用:

$python3prog.py--helpusage:prog.py[-h][-v|-q]xycalculateXtothepowerofYpositionalarguments:xthebaseytheexponentoptionalarguments:-h,--helpshowthishelpmessageandexit-v,--verbose-q,--quiet

总结

argparse模块提供的功能远远超出此处所示。它的文档非常详细和透彻,并有许多示例。相信你在阅读完本次的Python学习教程后,应该轻松消化它们,而不会感到不知所措。更多的Python学习教程也会继续为大家更新,伙伴们有不清楚的地方,可以留言!