15.2. argparse — 解析命令参数和选项

目标:命令行选项和参数解析

argparse 模块包含了用于创建命令行参数和选项程序的工具。它在 Python 2.7 中加入并作为 optparse 模块的替代者。argparse 模块的实现支持那些在 optparse 中不容易实现的特征,这也导致了向后不兼容的 API 改变,所以是一个新的模块被加入到标准库中。optparse 现在已经被废弃。

创建一个解析器

使用 argparse 的第一步是创建一个解析器对象然后告诉它要接收什么参数。解析器随后可以在程序运行时用于处理命令行参数。解析器对象 (ArgumentParser) 的构造器接收一些参数来构建用于帮助文本的简介和其他全局表现设置。

import argparse
parser = argparse.ArgumentParser(
    description='This is a PyMOTW sample program',
)

定义参数

argparse 是一个完整的参数处理库。参数可以触发不同动作,这可以通过 add_argument()action 参数来声明。支持的动作包括储存参数值(单独地或这作为列表的一部分存储),当使用一个参数时(包括特殊 true/false 的布尔型数值转换处理)存储一个常数,对参数出现次数进行计数和使用回调函数来使用定制的处理指令。

默认的动作是存储参数值。如果指定了参数类型,参数值会在被储存前转换。如果提供了 dest 参数,命令行参数被解析时参数值会以 dest 参数提供的变量名存储。

解析一个命令行

在所有参数都被定义后,通过传递一组参数字符串序列给 parse_args() 来解析参数。默认情况下,所有参数从 sys.argv[1:] 中取得,但也可以使用一个字符串列表。选项使用 GNU/POSIX 语法处理,所以选项和参数值在序列里是混合的。

parse_args() 的返回值是一个包含命令参数的 Namespace。这个对象将参数值作为属性保存,所以如果参数的 dest 被设为 "myoption",参数值可以通过 args.myoption 的方式获取。

简单的例子

以下是一个有三个不同选项的例子:一个布尔型选项 (-a), 一个简单的字符串选项 (-b), 和一个整数选项 (-c) 。

argparse_short.py

import argparse

parser = argparse.ArgumentParser(description='Short sample app')

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print(parser.parse_args(['-a', '-bval', '-c', '3']))

单字符选项传值有几种方式。前面的例子展示了两种不同的方式,-bval-c val

$ python3 argparse_short.py

Namespace(a=True, b='val', c=3)

c 相关的值在输出中是一个整数,这是因为 ArgumentParser 被告知在保存这个参数之前要进行转换。

名称多于一个字符的选项,也就是“长”选项也是以这样的方式处理的。

argparse_long.py

import argparse

parser = argparse.ArgumentParser(
    description='Example with long option names',
)

parser.add_argument('--noarg', action="store_true",
                    default=False)
parser.add_argument('--witharg', action="store",
                    dest="witharg")
parser.add_argument('--witharg2', action="store",
                    dest="witharg2", type=int)

print(
    parser.parse_args(
        ['--noarg', '--witharg', 'val', '--witharg2=3']
    )
)

结果是类似的。

$ python3 argparse_long.py

Namespace(noarg=True, witharg='val', witharg2=3)

argparse 是一个完整的命令行参数解析工具,可以处理可选参数和必须参数。

argparse_arguments.py

import argparse

parser = argparse.ArgumentParser(
    description='Example with nonoptional arguments',
)

parser.add_argument('count', action="store", type=int)
parser.add_argument('units', action="store")

print(parser.parse_args())

在这个例子中,count 参数值是一个整数,unit 参数值以一个字符串保存。如果命令行中缺少了两个参数中任何一个,或者传递的值不能被转换为正确的类型,将会抛出错误。

$ python3 argparse_arguments.py 3 inches

Namespace(count=3, units='inches')

$ python3 argparse_arguments.py some inches

usage: argparse_arguments.py [-h] count units
argparse_arguments.py: error: argument count: invalid int value:
'some'

$ python3 argparse_arguments.py

usage: argparse_arguments.py [-h] count units
argparse_arguments.py: error: the following arguments are
required: count, units

参数动作

当一个参数被传递时可以触发任何6个内建动作的任何一个。

store

选择性地转换为一个不同的类型后保存参数值。如果没有特别声明动作时这是默认动作。

store_const

保存一个在参数声明时定义的值,而非解析参数时得到的值。这通常被用于实现非布尔型数值的命令行标志。

store_true / store_false

保存相应的布尔型数值,这个动作被用于实现布尔值开关。

append

将参数值保存在一个列表中。如果参数重复了,那么多个参数值将会被保存。

append_const

将参数值保存在参数声明时指定的列表中。

version

打印程序版本详情然后退出

这个示例程序演示了让每个动作类型能够工作的最少配置。

argparse_action.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-s', action='store',
                    dest='simple_value',
                    help='Store a simple value')

parser.add_argument('-c', action='store_const',
                    dest='constant_value',
                    const='value-to-store',
                    help='Store a constant value')

parser.add_argument('-t', action='store_true',
                    default=False,
                    dest='boolean_t',
                    help='Set a switch to true')
parser.add_argument('-f', action='store_false',
                    default=True,
                    dest='boolean_f',
                    help='Set a switch to false')

parser.add_argument('-a', action='append',
                    dest='collection',
                    default=[],
                    help='Add repeated values to a list')

parser.add_argument('-A', action='append_const',
                    dest='const_collection',
                    const='value-1-to-append',
                    default=[],
                    help='Add different values to list')
parser.add_argument('-B', action='append_const',
                    dest='const_collection',
                    const='value-2-to-append',
                    help='Add different values to list')

parser.add_argument('--version', action='version',
                    version='%(prog)s 1.0')

results = parser.parse_args()
print('simple_value     = {!r}'.format(results.simple_value))
print('constant_value   = {!r}'.format(results.constant_value))
print('boolean_t        = {!r}'.format(results.boolean_t))
print('boolean_f        = {!r}'.format(results.boolean_f))
print('collection       = {!r}'.format(results.collection))
print('const_collection = {!r}'.format(results.const_collection))

 -t 和 -f 选项用于设置修改不同选项值,每个保存 True 或 False。-A-Bdest 参数值相同,所以它们的常量会被添加到相同的列表中。

$ python3 argparse_action.py -h

usage: argparse_action.py [-h] [-s SIMPLE_VALUE] [-c] [-t] [-f]
                          [-a COLLECTION] [-A] [-B] [--version]

optional arguments:
  -h, --help       show this help message and exit
  -s SIMPLE_VALUE  Store a simple value
  -c               Store a constant value
  -t               Set a switch to true
  -f               Set a switch to false
  -a COLLECTION    Add repeated values to a list
  -A               Add different values to list
  -B               Add different values to list
  --version        show program's version number and exit

$ python3 argparse_action.py -s value

simple_value     = 'value'
constant_value   = None
boolean_t        = False
boolean_f        = True
collection       = []
const_collection = []

$ python3 argparse_action.py -c

simple_value     = None
constant_value   = 'value-to-store'
boolean_t        = False
boolean_f        = True
collection       = []
const_collection = []

$ python3 argparse_action.py -t

simple_value     = None
constant_value   = None
boolean_t        = True
boolean_f        = True
collection       = []
const_collection = []

$ python3 argparse_action.py -f

simple_value     = None
constant_value   = None
boolean_t        = False
boolean_f        = False
collection       = []
const_collection = []

$ python3 argparse_action.py -a one -a two -a three

simple_value     = None
constant_value   = None
boolean_t        = False
boolean_f        = True
collection       = ['one', 'two', 'three']
const_collection = []

$ python3 argparse_action.py -B -A

simple_value     = None
constant_value   = None
boolean_t        = False
boolean_f        = True
collection       = []
const_collection = ['value-2-to-append', 'value-1-to-append']

$ python3 argparse_action.py --version

argparse_action.py 1.0

选项前缀

默认语法是基于使用前缀(“-”)表示命令行开关的Unix惯例。argparse 支持也其他前缀,所以程序可以使用符合本地操作系统默认的前缀(比如 windows 的 “'/'”)或者遵循不同的惯例。

argparse_prefix_chars.py

import argparse

parser = argparse.ArgumentParser(
    description='Change the option prefix characters',
    prefix_chars='-+/',
)

parser.add_argument('-a', action="store_false",
                    default=None,
                    help='Turn A off',
                    )
parser.add_argument('+a', action="store_true",
                    default=None,
                    help='Turn A on',
                    )
parser.add_argument('//noarg', '++noarg',
                    action="store_true",
                    default=False)

print(parser.parse_args())

你可以把 ArgumentParserprefix_chars 参数设置为一个包含所有允许的前缀的字符串。重要的一点是要理解尽管 prefix_chars 明确了允许的开关字符,但参数定义时指明了该参数的可用前缀。这样可以精确地控制选项功能,使用不同前缀可能仅仅是选项的别名(比如说平台依赖的命令行语法)或者其他意义(比如使用 '+' 来表示打开某个选项,'-' 来表示关闭某选项)。在上面的例子中,+a-a 是不同的参数,而 //noarg 可以使用 ++noarg 样式,但不能是 --noarg

$ python3 argparse_prefix_chars.py -h

usage: argparse_prefix_chars.py [-h] [-a] [+a] [//noarg]

Change the option prefix characters

optional arguments:
  -h, --help        show this help message and exit
  -a                Turn A off
  +a                Turn A on
  //noarg, ++noarg

$ python3 argparse_prefix_chars.py +a

Namespace(a=True, noarg=False)

$ python3 argparse_prefix_chars.py -a

Namespace(a=False, noarg=False)

$ python3 argparse_prefix_chars.py //noarg

Namespace(a=None, noarg=True)

$ python3 argparse_prefix_chars.py ++noarg

Namespace(a=None, noarg=True)

$ python3 argparse_prefix_chars.py --noarg

usage: argparse_prefix_chars.py [-h] [-a] [+a] [//noarg]
argparse_prefix_chars.py: error: unrecognized arguments: --noarg

参数来源

目前的例子里,解析器接收的参数列表来自于显式传递的一个列表,或默认从 sys.argv 中获取。显示传递一个列表给解析器在使用 argparse 来处理不来自命令行的但像命令行一样的指令(比如该命令来自配置文件)很有用。

argparse_with_shlex.py

import argparse
from configparser import ConfigParser
import shlex

parser = argparse.ArgumentParser(description='Short sample app')

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

config = ConfigParser()
config.read('argparse_with_shlex.ini')
config_value = config.get('cli', 'options')
print('Config  :', config_value)

argument_list = shlex.split(config_value)
print('Arg List:', argument_list)

print('Results :', parser.parse_args(argument_list))

这个例子使用 configparser 读取配置文件。

[cli]
options = -a -b 2

shlex 可以让拆开配置文件中的字符串变得容易。

$ python3 argparse_with_shlex.py

Config  : -a -b 2
Arg List: ['-a', '-b', '2']
Results : Namespace(a=True, b='2', c=None)

在应用程序代码中处理配置文件的另一个方案是使用 fromfile_prefix_chars 告诉 argparse 识别一个特殊参数,这个参数指明了包涵需要处理的参数的文件。

argparse_fromfile_prefix_chars.py

import argparse
import shlex

parser = argparse.ArgumentParser(description='Short sample app',
                                 fromfile_prefix_chars='@',
                                 )

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print(parser.parse_args(['@argparse_fromfile_prefix_chars.txt']))

这个例子的代码会在发现一个 @ 前缀的参数时停下,然后读取该文件来找到更多参数。这个文件应该每行包含一个参数,如下面例子所示。

argparse_fromfile_prefix_chars.txt

-a
-b
2

解析 argparse_from_prefix_chars.txt 的输出如下。

$ python3 argparse_fromfile_prefix_chars.py

Namespace(a=True, b='2', c=None)

帮助内容

自动生成的帮助文本

如果配置为这样做,argparse 会自动生成选项的帮助文本。ArgumentParseradd_help 参数可以控制相关选项。

argparse_with_help.py

import argparse

parser = argparse.ArgumentParser(add_help=True)

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print(parser.parse_args())

帮助选项(-h--help )默认会被自动添加,可以设置 add_helpFalse 来停用这个功能。

argparse_without_help.py

import argparse

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print(parser.parse_args())

尽管 -h--help 是请求帮助选项的事实标准,一些应用或某些 argparse 的用法不需要提供帮助内容或这两个选项另有他用。

$ python3 argparse_with_help.py -h

usage: argparse_with_help.py [-h] [-a] [-b B] [-c C]

optional arguments:
  -h, --help  show this help message and exit
  -a
  -b B
  -c C

$ python3 argparse_without_help.py -h

usage: argparse_without_help.py [-a] [-b B] [-c C]
argparse_without_help.py: error: unrecognized arguments: -h

定制帮助内容

对于需要直接处理帮助内容的应用,ArgumentParser 的一些实用方法在创建 定制动作 来打印带有额外信息的帮助文本时很有用。

argparse_custom_help.py

import argparse

parser = argparse.ArgumentParser(add_help=True)

parser.add_argument('-a', action="store_true", default=False)
parser.add_argument('-b', action="store", dest="b")
parser.add_argument('-c', action="store", dest="c", type=int)

print('print_usage output:')
parser.print_usage()
print()

print('print_help output:')
parser.print_help()

print_usage() 打印参数解析器的简略使用信息,print_help() 会打印完整帮助文本。

$ python3 argparse_custom_help.py

print_usage output:
usage: argparse_custom_help.py [-h] [-a] [-b B] [-c C]

print_help output:
usage: argparse_custom_help.py [-h] [-a] [-b B] [-c C]

optional arguments:
  -h, --help  show this help message and exit
  -a
  -b B
  -c C

ArgumentParser 使用一个格式化类来控制帮助文本的显示样式。要改变格式化类,可以在实例化 ArgumentParser 时传递 formatter_class 参数。

例如,RawDescriptionHelpFormatter 会绕过程序默认提供的换行。

argparse_raw_description_help_formatter.py

import argparse

parser = argparse.ArgumentParser(
    add_help=True,
    formatter_class=argparse.RawDescriptionHelpFormatter,
    description="""
    description
        not
           wrapped""",
    epilog="""
    epilog
      not
         wrapped""",
)

parser.add_argument(
    '-a', action="store_true",
    help="""argument
    help is
    wrapped
    """,
)

parser.print_help()

命令的介绍和结语的帮助文本会原封不动地打印。

$ python3 argparse_raw_description_help_formatter.py

usage: argparse_raw_description_help_formatter.py [-h] [-a]

    description
        not
           wrapped

optional arguments:
  -h, --help  show this help message and exit
  -a          argument help is wrapped

    epilog
      not
         wrapped

 RawTextHelpFormatter 视所有帮助文本都是预格式化好的。

argparse_raw_text_help_formatter.py

import argparse

parser = argparse.ArgumentParser(
    add_help=True,
    formatter_class=argparse.RawTextHelpFormatter,
    description="""
    description
        not
           wrapped""",
    epilog="""
    epilog
      not
         wrapped""",
)

parser.add_argument(
    '-a', action="store_true",
    help="""argument
    help is not
    wrapped
    """,
)

parser.print_help()

-a 的帮助文本不会再别整齐地包装起来。

$ python3 argparse_raw_text_help_formatter.py

usage: argparse_raw_text_help_formatter.py [-h] [-a]

    description
        not
           wrapped

optional arguments:
  -h, --help  show this help message and exit
  -a          argument
                  help is not
                  wrapped

    epilog
      not
         wrapped

原始格式化类可能用于改变帮助文本的格式可能会让例子失效的应用程序中。

 MetavarTypeHelpFormatter 还会打印每个选项的类型而非仅有目标变量名称,这个格式化类可用于有很多不同类型选项的应用程序。

argparse_metavar_type_help_formatter.py

import argparse

parser = argparse.ArgumentParser(
    add_help=True,
    formatter_class=argparse.MetavarTypeHelpFormatter,
)

parser.add_argument('-i', type=int, dest='notshown1')
parser.add_argument('-f', type=float, dest='notshown2')

parser.print_help()

除了显示 dest 的值,与选项相关的参数值类型也会被打印。

$ python3 argparse_metavar_type_help_formatter.py

usage: argparse_metavar_type_help_formatter.py [-h] [-i int] [-f
 float]

optional arguments:
  -h, --help  show this help message and exit
  -i int
  -f float

组织解析器

为了让实现解析器或提高帮助文本的可用性, argparse 模块提供几个功能来组织参数解析器。

分享解析规则

程序员通常需要实现一套命令行工具,这个工具接收一组参数,并以某特定方式使用这组参数。例如,一些程序需要在真正执行动作前对用户进行认证,它们都需要支持 --user--password 选项。除了单独地向每个 ArgumentParser 都添加这两个选项外,先定义一个带有共享参数的父解析器,接着继承父解析器并编写用于某特定程序的参数解析器也是一种可能的方案。

第一步是建立有共享参数解析器。由于后续使用父解析器的用户会尝试添加相同的帮助选项,这将会抛出错误,所以父解析器自动生成帮助文本特性被关闭了。

argparse_parent_base.py

import argparse

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument('--user', action="store")
parser.add_argument('--password', action="store")

接下来创建一个使用共享 parents 参数集的解析器:

argparse_uses_parent.py

import argparse
import argparse_parent_base

parser = argparse.ArgumentParser(
    parents=[argparse_parent_base.parser],
)

parser.add_argument('--local-arg',
                    action="store_true",
                    default=False)

print(parser.parse_args())

最后程序会有所有的三个选项。

$ python3 argparse_uses_parent.py -h

usage: argparse_uses_parent.py [-h] [--user USER]
                               [--password PASSWORD]
                               [--local-arg]

optional arguments:
  -h, --help           show this help message and exit
  --user USER
  --password PASSWORD
  --local-arg

冲突选项

上面的例子指出添加两个参数名相同的选项会导致异常。冲突解决行为可以通过传递一个 confilct_handler 来改变。两个内建的处理器是 error (默认)和 resolve ,这处理器会根据同名参数的添加顺序选择参数处理器,即后续同名参数会覆盖之前的同名参数。

argparse_conflict_handler_resolve.py

import argparse

parser = argparse.ArgumentParser(conflict_handler='resolve')

parser.add_argument('-a', action="store")
parser.add_argument('-b', action="store", help='Short alone')
parser.add_argument('--long-b', '-b',
                    action="store",
                    help='Long and short together')

print(parser.parse_args(['-h']))

在这个例子中,由于最后一个参数处理器使用了已经定义过的参数名,单独选项 -b 会被 --long-b 的别名掩盖。

$ python3 argparse_conflict_handler_resolve.py

usage: argparse_conflict_handler_resolve.py [-h] [-a A]
[--long-b LONG_B]

optional arguments:
  -h, --help            show this help message and exit
  -a A
  --long-b LONG_B, -b LONG_B
                        Long and short together

交换 add_argument() 的调用顺序可以独立的 -b 不被掩盖:

argparse_conflict_handler_resolve2.py

import argparse

parser = argparse.ArgumentParser(conflict_handler='resolve')

parser.add_argument('-a', action="store")
parser.add_argument('--long-b', '-b',
                    action="store",
                    help='Long and short together')
parser.add_argument('-b', action="store", help='Short alone')

print(parser.parse_args(['-h']))

现在两个选项可以一起使用了。

$ python3 argparse_conflict_handler_resolve2.py

usage: argparse_conflict_handler_resolve2.py [-h] [-a A]
                                             [--long-b LONG_B]
                                             [-b B]

optional arguments:
  -h, --help       show this help message and exit
  -a A
  --long-b LONG_B  Long and short together
  -b B             Short alone

参数组

argparse 把参数定义组合为 “参数组” 。默认情况下,它使用两个参数组,一个用于可选参数,一个用于位置参数。

argparse_default_grouping.py

import argparse

parser = argparse.ArgumentParser(description='Short sample app')

parser.add_argument('--optional', action="store_true",
                    default=False)
parser.add_argument('positional', action="store")

print(parser.parse_args())

位置参数和可选参数的分组反映在帮助文本的不同区块。

$ python3 argparse_default_grouping.py -h

usage: argparse_default_grouping.py [-h] [--optional] positional

Short sample app

positional arguments:
  positional

optional arguments:
  -h, --help  show this help message and exit
  --optional

分组可以改变以让帮助文本更有逻辑性,这样可以让相关的选项和参数值会被一起归档。之前的共享选项例子可以使用定制分组以让认证选项一起出现在帮助文本中。

使用 add_argument_group 创建 “authentication” 分组,然后向分组添加认证相关选项到这个分组中,而非基础解析器。

argparse_parent_with_group.py

import argparse

parser = argparse.ArgumentParser(add_help=False)

group = parser.add_argument_group('authentication')

group.add_argument('--user', action="store")
group.add_argument('--password', action="store")

正如之前的用法,程序可以使用基于分组的父解析器作为 parents 的值。

argparse_uses_parent_with_group.py

import argparse
import argparse_parent_with_group

parser = argparse.ArgumentParser(
    parents=[argparse_parent_with_group.parser],
)

parser.add_argument('--local-arg',
                    action="store_true",
                    default=False)

print(parser.parse_args())

现在帮助文本会把认证选项显示在一起。

$ python3 argparse_uses_parent_with_group.py -h

usage: argparse_uses_parent_with_group.py [-h] [--user USER]
                                          [--password PASSWORD]
                                          [--local-arg]

optional arguments:
  -h, --help           show this help message and exit
  --local-arg

authentication:
  --user USER
  --password PASSWORD

互斥选项

定义互斥选项是使用分组特性的特殊情况,这时需要使用 add_mutually_exclusive_group() 而不是 add_argument_group()

argparse_mutually_exclusive.py

import argparse

parser = argparse.ArgumentParser()

group = parser.add_mutually_exclusive_group()
group.add_argument('-a', action='store_true')
group.add_argument('-b', action='store_true')

print(parser.parse_args())

argparse 会保证互斥性,这会让组内只有一个选项可以使用。

$ python3 argparse_mutually_exclusive.py -h

usage: argparse_mutually_exclusive.py [-h] [-a | -b]

optional arguments:
  -h, --help  show this help message and exit
  -a
  -b

$ python3 argparse_mutually_exclusive.py -a

Namespace(a=True, b=False)

$ python3 argparse_mutually_exclusive.py -b

Namespace(a=False, b=True)

$ python3 argparse_mutually_exclusive.py -a -b

usage: argparse_mutually_exclusive.py [-h] [-a | -b]
argparse_mutually_exclusive.py: error: argument -b: not allowed
with argument -a

嵌套解析器

之前介绍的使用父解析器是在相关命令之间共享选项的一种方法。一个替代方案是将这些命令组合到单个程序中,使用子解析器来处理命令行的每个部分。这样程序就如 svnhg 和其他有多个命令动作作或子命令的程序一样工作。

一个处理系统目录的程序可能会定义创建,删除和列举目录内容的命令。

argparse_subparsers.py

import argparse

parser = argparse.ArgumentParser()

subparsers = parser.add_subparsers(help='commands')

# 一个列举命令
list_parser = subparsers.add_parser(
    'list', help='List contents')
list_parser.add_argument(
    'dirname', action='store',
    help='Directory to list')

# 一个创建命令
create_parser = subparsers.add_parser(
    'create', help='Create a directory')
create_parser.add_argument(
    'dirname', action='store',
    help='New directory to create')
create_parser.add_argument(
    '--read-only', default=False, action='store_true',
    help='Set permissions to prevent writing to the directory',
)

# 一个删除命令
delete_parser = subparsers.add_parser(
    'delete', help='Remove a directory')
delete_parser.add_argument(
    'dirname', action='store', help='The directory to remove')
delete_parser.add_argument(
    '--recursive', '-r', default=False, action='store_true',
    help='Remove the contents of the directory, too',
)

print(parser.parse_args())

帮助输出将命名的子解析器显示为“命令”,这个“命令”可以在命令行中将其指定为位置参数。

$ python3 argparse_subparsers.py -h

usage: argparse_subparsers.py [-h] {list,create,delete} ...

positional arguments:
  {list,create,delete}  commands
    list                List contents
    create              Create a directory
    delete              Remove a directory

optional arguments:
  -h, --help            show this help message and exit

每个子解析器也可用拥有它自己的帮助文本,用于介绍该命令的参数选项。

$ python3 argparse_subparsers.py create -h

usage: argparse_subparsers.py create [-h] [--read-only] dirname

positional arguments:
  dirname      New directory to create

optional arguments:
  -h, --help   show this help message and exit
  --read-only  Set permissions to prevent writing to the directo
ry

当解析参数时,parse_args() 返回的 Namespace 对象只包含命令相关的值。

$ python3 argparse_subparsers.py delete -r foo

Namespace(dirname='foo', recursive=True)

高级参数处理

目前的例子展示了简单的布尔标志,字符串选项或数值参数,还有位置参数。argparse 也支持用于变长参数列表,枚举和常量的复杂参数定义。

变长参数列表

单个参数定义可以被配置于用来接收来自命令行的多个参数。根据需要或期望的参数个数把 nargs 设为下面表格中的一个。

argparse 变长参数定义标志

含义
N 参数确切个数(比如 3 个)
? 0 或 1 个参数
* 0 或更多参数
+ 至少有一个参数

argparse_nargs.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('--three', nargs=3)
parser.add_argument('--optional', nargs='?')
parser.add_argument('--all', nargs='*', dest='all')
parser.add_argument('--one-or-more', nargs='+')

print(parser.parse_args())

解析器强制执行参数计数指令,并生成确切的语法图作为命令帮助文本的一部分。

$ python3 argparse_nargs.py -h

usage: argparse_nargs.py [-h] [--three THREE THREE THREE]
                [--optional [OPTIONAL]]
                [--all [ALL [ALL ...]]]
                [--one-or-more ONE_OR_MORE [ONE_OR_MORE ...]]

optional arguments:
  -h, --help            show this help message and exit
  --three THREE THREE THREE
  --optional [OPTIONAL]
  --all [ALL [ALL ...]]
  --one-or-more ONE_OR_MORE [ONE_OR_MORE ...]

$ python3 argparse_nargs.py

Namespace(all=None, one_or_more=None, optional=None, three=None)

$ python3 argparse_nargs.py --three

usage: argparse_nargs.py [-h] [--three THREE THREE THREE]
                [--optional [OPTIONAL]]
                [--all [ALL [ALL ...]]]
                [--one-or-more ONE_OR_MORE [ONE_OR_MORE ...]]
argparse_nargs.py: error: argument --three: expected 3
argument(s)

$ python3 argparse_nargs.py --three a b c

Namespace(all=None, one_or_more=None, optional=None,
three=['a', 'b', 'c'])

$ python3 argparse_nargs.py --optional

Namespace(all=None, one_or_more=None, optional=None, three=None)

$ python3 argparse_nargs.py --optional with_value

Namespace(all=None, one_or_more=None, optional='with_value',
three=None)

$ python3 argparse_nargs.py --all with multiple values

Namespace(all=['with', 'multiple', 'values'], one_or_more=None,
optional=None, three=None)

$ python3 argparse_nargs.py --one-or-more with_value

Namespace(all=None, one_or_more=['with_value'], optional=None,
three=None)

$ python3 argparse_nargs.py --one-or-more with multiple values

Namespace(all=None, one_or_more=['with', 'multiple', 'values'],
optional=None, three=None)

$ python3 argparse_nargs.py --one-or-more

usage: argparse_nargs.py [-h] [--three THREE THREE THREE]
                [--optional [OPTIONAL]]
                [--all [ALL [ALL ...]]]
                [--one-or-more ONE_OR_MORE [ONE_OR_MORE ...]]
argparse_nargs.py: error: argument --one-or-more: expected
at least one argument

参数类型

argparse 将所有参数视为字符串,除非被告知要将参数转换为其他类型。add_argument()type() 参数定义了一个转换函数,这个函数会被 ArgumentParser 用来把参数值转换为其他类型。

argparse_type.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-i', type=int)
parser.add_argument('-f', type=float)
parser.add_argument('--file', type=open)

try:
    print(parser.parse_args())
except IOError as msg:
    parser.error(str(msg))

任何接收一个字符串的可调用对象都可以被传递为 type,包括内建类型比如 intfloat 甚至 open()

$ python3 argparse_type.py -i 1

Namespace(f=None, file=None, i=1)

$ python3 argparse_type.py -f 3.14

Namespace(f=3.14, file=None, i=None)

$ python3 argparse_type.py --file argparse_type.py

Namespace(f=None, file=<_io.TextIOWrapper
name='argparse_type.py' mode='r' encoding='UTF-8'>, i=None)

如果类型转换失败,argparser 会抛出一个异常。TypeErrorValueError 会被自动捕捉并转换为简单的错误信息提供给用户。其他错误,比如下面例子里输入文件不存在导致的 IOError 必须由调用者处理。

$ python3 argparse_type.py -i a

usage: argparse_type.py [-h] [-i I] [-f F] [--file FILE]
argparse_type.py: error: argument -i: invalid int value: 'a'

$ python3 argparse_type.py -f 3.14.15

usage: argparse_type.py [-h] [-i I] [-f F] [--file FILE]
argparse_type.py: error: argument -f: invalid float value:
'3.14.15'

$ python3 argparse_type.py --file does_not_exist.txt

usage: argparse_type.py [-h] [-i I] [-f F] [--file FILE]
argparse_type.py: error: [Errno 2] No such file or directory:
'does_not_exist.txt'

要限制参数值为预先定义的一组值,可以使用 choices 参数。

argparse_choices.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument(
    '--mode',
    choices=('read-only', 'read-write'),
)

print(parser.parse_args())

如果 --mode 的参数值不是允许值之一,程序会抛出错误并停止。

$ python3 argparse_choices.py -h

usage: argparse_choices.py [-h] [--mode {read-only,read-write}]

optional arguments:
  -h, --help            show this help message and exit
  --mode {read-only,read-write}

$ python3 argparse_choices.py --mode read-only

Namespace(mode='read-only')

$ python3 argparse_choices.py --mode invalid

usage: argparse_choices.py [-h] [--mode {read-only,read-write}]
argparse_choices.py: error: argument --mode: invalid choice:
'invalid' (choose from 'read-only', 'read-write')

文件参数

尽管 file 对象可以通过一个字符串参数实例化,但这个对象没有包含访问模式参数。FileType 提供了一种更灵活的方式来指定一个参数是文件,并且包含了访问模式和缓冲区大小。

argparse_FileType.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-i', metavar='in-file',
                    type=argparse.FileType('rt'))
parser.add_argument('-o', metavar='out-file',
                    type=argparse.FileType('wt'))

try:
    results = parser.parse_args()
    print('Input file:', results.i)
    print('Output file:', results.o)
except IOError as msg:
    parser.error(str(msg))

解析后的参数值是被打开文件的句柄。应用程序要负责不需要文件后关闭文件。

$ python3 argparse_FileType.py -h

usage: argparse_FileType.py [-h] [-i in-file] [-o out-file]

optional arguments:
  -h, --help   show this help message and exit
  -i in-file
  -o out-file

$ python3 argparse_FileType.py -i argparse_FileType.py -o tmp_\
file.txt

Input file: <_io.TextIOWrapper name='argparse_FileType.py'
mode='rt' encoding='UTF-8'>
Output file: <_io.TextIOWrapper name='tmp_file.txt' mode='wt'
encoding='UTF-8'>

$ python3 argparse_FileType.py -i no_such_file.txt

usage: argparse_FileType.py [-h] [-i in-file] [-o out-file]
argparse_FileType.py: error: argument -i: can't open
'no_such_file.txt': [Errno 2] No such file or directory:
'no_such_file.txt'

定制动作

除了之前介绍的内建动作,还可以通过提供实现 Action API 的对象来定义自定义动作。传递给 add_argument() 作为 action 的对象应该接受被定义参数的所有参数(也就是传递给 add_argument 的所有参数)并返回一个可调用对象,这个可调用对象接收 parser 作为参数解析器,namespace 持有参数解析结果,value 为参数值,和触发动作的 option_string

一个 Action 类提供了定义新动作的便捷起点。Action 的构造器会处理参数定义,所以子类只需要重写 __call__()

argparse_custom_action.py

import argparse

class CustomAction(argparse.Action):
    def __init__(self,
                 option_strings,
                 dest,
                 nargs=None,
                 const=None,
                 default=None,
                 type=None,
                 choices=None,
                 required=False,
                 help=None,
                 metavar=None):
        argparse.Action.__init__(self,
                                 option_strings=option_strings,
                                 dest=dest,
                                 nargs=nargs,
                                 const=const,
                                 default=default,
                                 type=type,
                                 choices=choices,
                                 required=required,
                                 help=help,
                                 metavar=metavar,
                                 )
        print('Initializing CustomAction')
        for name, value in sorted(locals().items()):
            if name == 'self' or value is None:
                continue
            print('  {} = {!r}'.format(name, value))
        print()
        return

    def __call__(self, parser, namespace, values,
                 option_string=None):
        print('Processing CustomAction for {}'.format(self.dest))
        print('  parser = {}'.format(id(parser)))
        print('  values = {!r}'.format(values))
        print('  option_string = {!r}'.format(option_string))

        # 对输入值做一些处理
        if isinstance(values, list):
            values = [v.upper() for v in values]
        else:
            values = values.upper()
        # 以传递给构造器的目标变量保存结果
        # variable given to our constructor.
        setattr(namespace, self.dest, values)
        print()

parser = argparse.ArgumentParser()

parser.add_argument('-a', action=CustomAction)
parser.add_argument('-m', nargs='*', action=CustomAction)

results = parser.parse_args(['-a', 'value',
                             '-m', 'multivalue',
                             'second'])
print(results)

value 的类型取决于 nargs 的值。如果参数运行多重值,value 将会是一个列表即使它只包含一个元素。

option_string 的值同样决定于原始参数定义。对于位置参数,option_string 一直是 None

$ python3 argparse_custom_action.py

Initializing CustomAction
  dest = 'a'
  option_strings = ['-a']
  required = False

Initializing CustomAction
  dest = 'm'
  nargs = '*'
  option_strings = ['-m']
  required = False

Processing CustomAction for a
  parser = 4315836992
  values = 'value'
  option_string = '-a'

Processing CustomAction for m
  parser = 4315836992
  values = ['multivalue', 'second']
  option_string = '-m'

Namespace(a='VALUE', m=['MULTIVALUE', 'SECOND'])

另见

本文章首发在 LearnKu.com 网站上。
上一篇 下一篇
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~