>>> from env_helper import info; info()
页面更新时间: 2023-12-27 09:29:51
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-16-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

4.6. 使用argparse处理命令行参数

尽管应用程序通常能够通过配置文件在不修改代码的情况下改变行为,但提供灵活易用 的命令行参数依然非常有意义,比如t减轻用户的学习成本,通常命令行参数的用法只需要 在应用程序名后面加--help参数就能获得,而配置文件的配置方法通常需要通读手册才能掌 握;同一个运行环境中有多个配置文件存在,那么需要通过命令行参数指定当前使用哪一个 配置文件,如pylint的--rcfile参数就是做这个事的。

为了做好命令行处理这件事,Pythonista尝试好几个方案,标准库中留下的getoptoptparseargparse就是证明。其中getopt是类似UNIX系统中getopt()这个C函数的实现, 可以处理长短配置项和参数> 如有命令行参数-a -b -cfoo -d bar a1 a2,在处理之后的结果是 两个列表,其中一个是配置项列表[('-a',''), ('-b',''), ('-c','foo'), ('-d','bar')],每一个元素都由配 置项名和其值(默认为空字符串)组成;另一个是参数列表['a1','a2'],每一个元素都是一个 参数值。 getopt的问题在于两点,一个是长短配置项需要分开处理,二是对非法参数和必填参数的处理需要手动。如:

>>> import getopt
>>> import sys
>>> try:
>>>     opts, args = getopt.getopt (sys.argv [1:],"ho:v",["help", "outputs="])
>>> except getopt.GetoptError as err:
>>>     print (str (err)) # 此处输出类似"option -a not recognized"的出雄信,|!、 usage ()
>>>     sys.exit(2)
>>> cutpvt = None
>>> verbose = False
>>> for o, a in opts:
>>>     if o == "-v":
>>>         verbose = True
>>>     elif o in ("--help","-h"):
>>>         usage()
>>>         sys.exit ()
>>>     elif o in ("-o%" ,"--output11") :
>>>         output = a
>>>     else:
>>>         assert False," nufihandled option**"
option -f not recognized
An exception has occurred, use %tb to see the full traceback.


SystemExit: 2
/usr/lib/python3/dist-packages/IPython/core/interactiveshell.py:2886: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
  warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)

从for循环处可以看到,这种处理非常原始和不便,而从getopt.getopt(sys.argv[1:], "ho:v","help", "output="])函数调用时的,"ho:v"["help”,"output"],两个实参可以看出,要编写和维护还是比较闲难的,所以optparse就登场了。 optparsegetopt要更加方便、强劲, 与C风格的getopt不同t它采用的是声明式风格,此外,它还能够自动生成应用程序的帮助 信息下面是一个例子:

>>> from optparse import OptionParser
>>> parser = OptionParser()
>>> parser.add_option("-f", "--file", dest="filenamen",help="write report to FILE", metavar="FILE")
>>> parser.add_option("-q", "--quiet",action="store_false", dest="verbose", default=True, help="don't print status messages to stdout")
>>> #{options, args} = parser.parse_args()
<Option at 0x7efdac74dd90: -q/--quiet>

可以看到add_option()方法非常强大,同时支持长短配置项,还有默认值、帮助信息 等,简单的几行代码,可以支持非常丰富的命令行接口。如,以下几个都是合法的应用程序 调用:

<yourscript> -f outfile —quiet <yourscript> --quiet —file outfile <yourscript> -q -foutfile <yourscript〉-qfoutfile

除此之外.虽然没有声明帮助参数.但默认给加上了-h或–help支持,通过这两个参数 调用应用稃序,可以看到自动生成的帮助信息。

Usage: <yourscript> [options] Options;

-h,--help show this help message and exit -f FILE,--file=FILE write report to FILE -q, --quiet don't print status messages to stdout

不过otparse虽然很好,但是后来出现的argparse在继承了它声明式风格的优点之外, 又多了更丰富的功能,所以现阶段最好用的参数处理标准库是argparse,使optparse成为了 —个被弃用的库。

因为argparseoptparse脱胎而来,所以用法倒也大致相同,都是先生成一个parser实例,然后增加参数声明。如上文中getopt的那个例子,可以用其改造为如下形式:

import argparse parser = argparse.ArgumentParser() parser.add_argument('-o', '--output') parser.add_argument('-v', dest='verbose', action='store_true') args = parser.parse_args()

可以看到,代码大大地减化了,代码更少,bug更少。与optparse中的add_option()类 似,add_argument()方法用以增加一个参数声明。与add_option()相比,它有儿个方面的改进, 其中之一就是支持类型增多,而且语法更加直观。表现在type参数的值不再是一个字符串, 而是一个可调用对象,比如在add_option()调用时是type="int",而在add_argurnent()调用时 直接写type=int就可以了。除了支持常规的int/float等基本数值类型外,argparse还支持文件 类型,只要参数合法,程序就能够使用相应的文件描述符。如:

>>> import argparse
>>> parser=argparse.ArgumentParser()
>>> parser.add_argument ('bar' , type=argparse.FileType ('w'))
>>> parser .parse_args (['out.txt'])
Namespace(bar=<_io.TextIOWrapper name='out.txt' mode='w' encoding='UTF-8'>)

另外,扩展类型也变得更加容易,任何可调用对象,比如函数,都可以作为type的实 参a与type类似,Choices参数也支持更多的类型,而不是像add_option那样只有字符串。 比如下面这句代码是合法的:

>>> parser.add_argument('door', type=int, choices=range(1, 4))
_StoreAction(option_strings=[], dest='door', nargs=None, const=None, default=None, type=<class 'int'>, choices=range(1, 4), required=True, help=None, metavar=None)

此外,add_argument()提供了对必填参数的支持,只要把required参数设置为True传递 进去,当缺失这一参数时,argparse就会自动退出程序,并提示用户。

如果仅仅是add_argument()add_option()更加强大一点,并不足以让它把optparse踢 出标准库,ArgumentParser还支持参数分组。add_argument_group()可以在输出帮助信息时更 加清晰,这在用法复杂的CLI应用程序中非常有帮助,比如setuptools配套的setup.py文件, 如果运行python setiip.py help可以看到它的参数是分组的。·下面是一个简单的示例:

>>> parser=argparse.ArgumentParser(prog="PROG",add_help=False)
>>> group1=parser.add_argument_group("group1","group1 description")
>>> group1.add_argument("foo",help="foo help")
>>> group2=parser.add_argument_group("group2", 'group2 description1')
>>> group2 .add_argument ('--bar' ,help= 'bar help')
>>> parser.print_help()
usage: PROG [--bar BAR] foo

group1:
  group1 description

  foo        foo help

group2:
  group2 description1

  --bar BAR  bar help

如果仅仅是更加漂亮的帮助信息输出不够吸引你,那么add_mutually_exclusive_group (required=False)就非常实用:它确保组中的参数至少有一个或者只有一个(required=Trae)

argparse 也支持子命令》比如 pip 就有 install/uninstall/freeze/list/show 等子命令, 这些子命令又接受不同的参数,使用ArgumentParser.add_subparsers()就可以实现类似的功能。

>>> import argparse
>>> parser=argparse .ArgumentParser(prog= 'PROG')
>>> subparsers = parser.add_subparsers (help= 'sub-command help')
>>> parser_a = subparsers. add_parser ('a', help=" a help")
>>> parser_a.add_argument('--bar', type=int, help='bar help')
>>> parser .parse_args(['a',"--bar","1"])
Namespace(bar=1)

看,就是这么简单!除了参数处理之外,当出现非法参数时,用户还需要做一些处理, 处理完成后.一般是输出提示信息并退出应用程序。ArgumentParser提供了两个方法函数, 分别是 exit(status=0, message=None)error(message),可以省了 import sys 再调用 sys.exit() 的步骤。