Python argparse-向多个子解析器添加参数


69

我的脚本定义了一个主解析器和多个子解析器。我想将-p论点应用于一些子解析器。到目前为止,代码如下所示:

parser = argparse.ArgumentParser(prog="myProg")
subparsers = parser.add_subparsers(title="actions")

parser.add_argument("-v", "--verbose",
                    action="store_true",
                    dest="VERBOSE",
                    help="run in verbose mode")

parser_create = subparsers.add_parser ("create", 
                                        help = "create the orbix environment")
parser_create.add_argument ("-p", 
                            type = int, 
                            required = True, 
                            help = "set db parameter")

# Update
parser_update = subparsers.add_parser ("update", 
                                        help = "update the orbix environment")
parser_update.add_argument ("-p", 
                            type = int, 
                            required = True, 
                            help = "set db parameter")

如您所见,add_arument ("-p")重复了两次。实际上,我还有更多的次级解析器。有没有一种方法可以遍历现有的子解析器以避免重复?

作为记录,我正在使用Python 2.7


Answers:


82

这可以通过定义一个包含公共选项的父解析器来实现:

import argparse

parent_parser = argparse.ArgumentParser(description="The parent parser")
parent_parser.add_argument("-p", type=int, required=True,
                           help="set db parameter")
subparsers = parent_parser.add_subparsers(title="actions")
parser_create = subparsers.add_parser("create", parents=[parent_parser],
                                      add_help=False,
                                      description="The create parser",
                                      help="create the orbix environment")
parser_create.add_argument("--name", help="name of the environment")
parser_update = subparsers.add_parser("update", parents=[parent_parser],
                                      add_help=False,
                                      description="The update parser",
                                      help="update the orbix environment")

这将生成以下格式的帮助消息:

parent_parser.print_help()

输出:

usage: main.py [-h] -p P {create,update} ...
The parent parser
optional arguments:
  -h, --help       show this help message and exit
  -p P             set db parameter
actions:
  {create,update}
    create         create the orbix environment
    update         update the orbix environment
parser_create.print_help()

输出:

usage: main.py create [-h] -p P [--name NAME] {create,update} ...
The create parser
optional arguments:
  -h, --help       show this help message and exit
  -p P             set db parameter
  --name NAME      name of the environment
actions:
  {create,update}
    create         create the orbix environment
    update         update the orbix environment

但是,如果您运行程序,则如果未指定操作(即createupdate),则不会遇到错误。如果您希望这种行为,请按如下所示修改您的代码。

<...>
subparsers = parent_parser.add_subparsers(title="actions")
subparsers.required = True
subparsers.dest = 'command'
<...>

此修复程序是在此SO问题中提出的该问题涉及跟踪拉动请求的问题。

由@hpaulj更新

由于自2011年以来处理次解析器的变化,将主解析器用作并不是一个好主意parent。更一般而言,不要尝试dest在主解析器和子解析器中定义相同的参数(相同)。子解析器的值将覆盖主设置的所有内容(即使子解析器default也可以这样做)。创建单独的解析器以用作parents。并且如文档中所示,父母应使用add_help=False


15
文档中:“当从子解析器请求帮助消息时,只会打印该特定解析器的帮助。该帮助消息将不包括父解析器或同级解析器消息。” 这似乎是该策略的主要缺点。
Ryne Everett

我最终通过帮助消息向根解析器添加了一个虚拟参数。
Ryne Everett

2
@RyneEverett:该手册部分令人困惑并且可能已经过时,因为至少在Python 3.5.3中--help,子解析器似乎包含来自父解析器的参数。
user1338062

我已经编辑了显示--help父级和子级解析器输出的答案。请注意,帮助消息create同时显示“创建”和所有操作,这是不正确的。循环的答案不会受此问题的困扰
Craymichael

5
我认为它表现异常的原因是“父解析器”实际上与“带有子解析器的解析器”不是同一回事。(请注意,文档永远不会同时显示这两个功能)肯定要使用顶级解析器,带有全局args的单独“父”解析器,以及前者的子解析器(将其parents=设置为后者)。
Andrew

31

接受的答案是正确的; 正确的方法是使用父解析器。但是,示例代码IMO并没有真正解决问题。让我加上几分钱,提供一个更合适的例子。

可接受答案的主要区别在于,明确的可能性是--verbose仅对某些子解析器(-p仅对于create和子update解析器,而不对其他子解析器)具有一些根级参数(例如)和共享参数

# Same main parser as usual
parser = argparse.ArgumentParser()

# Usual arguments which are applicable for the whole script / top-level args
parser.add_argument('--verbose', help='Common top-level parameter',
                    action='store_true', required=False)

# Same subparsers as usual
subparsers = parser.add_subparsers(help='Desired action to perform', dest='action')

# Usual subparsers not using common options
parser_other = subparsers.add_parser("extra-action", help='Do something without db')

# Create parent subparser. Note `add_help=False` and creation via `argparse.`
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('-p', help='add db parameter', required=True)

# Subparsers based on parent

parser_create = subparsers.add_parser("create", parents=[parent_parser],
                                      help='Create something')
# Add some arguments exclusively for parser_create

parser_update = subparsers.add_parser("update", parents=[parent_parser],
                                      help='Update something')
# Add some arguments exclusively for parser_update 

这是顶级帮助消息(请注意,-p此处未显示参数,正是您所期望的,因为它特定于某些子解析器):

>>> parser.print_help()
usage: [-h] [--verbose] {extra-action,create,update} ...

positional arguments:
  {extra-action,create,update}
                        Desired action to perform
    extra-action        Do something without db
    create              Create something
    update              Update something

optional arguments:
  -h, --help            show this help message and exit
  --verbose             Common top-level parameter

以及该操作的帮助消息create

>>> parser_create.print_help()
usage:  create [-h] -p P

optional arguments:
  -h, --help  show this help message and exit
  -p P        add db parameter

因此,在我对主要答案的先前编辑中,我在parent解析器中包括“ -p”选项,以显示用户在所有子解析器之间如何具有通用选项。然后,我表明可以将“ --name”选项添加到命令/操作subparser唯一的单个create命令中。我们解决方案的主要区别在于显示帮助的方式。这是我的输出,这是你的输出
craymichael

@craymichael,您不需要父级解析器为所有参数添加参数(这可以通过普通解析器完成);)不同的帮助只是此处更大差异的一个例证。顺便说一句,我还添加了帮助输出到我的答案。
教父

1
我完全同意,这不是必需的,但有些人可能想同时使用两者。您的帮助信息绝对干净,感谢您的添加!
craymichael

3
这是一个非常清楚的例子,这个答案需要更多的支持。
David Parks

1
@大象,不,子解析器的动作是互斥的。首先运行--verbose create -p 2,然后运行--verbose update -p 3
The Godfather

9

您还可以遍历子解析器,并向所有子解析器添加相同的选项。

parser = argparse.ArgumentParser(prog="myProg")
subparsers = parser.add_subparsers(title="actions")
parser.add_argument("-v", "--verbose",
                    action="store_true",
                    dest="VERBOSE",
                    help="run in verbose mode")

parser_create = subparsers.add_parser ("create", 
                                        help = "create the orbix environment")
parser_update = subparsers.add_parser ("update", 
                                        help = "update the orbix environment")

for subparser in [parser_create, parser_update]:
    subparser.add_argument ("-p", 
                            type = int, 
                            required = True, 
                            help = "set db parameter")

2
我认为这是一个不好的方法,因为您需要循环遍历参数。Sven Marnach的答案更加干燥和实用。
NeoMorfeo 2015年

5

您可以按以下方式遍历子解析器。

for name, subp in subparsers.choices.items():
    print(subp)
    subp.add_argument(dest='g', help='Input for g variable', default=7, type=int)

请注意,通过使用subparsers.choices它,您无需对所有子解析器进行硬编码。


这与@JanK的答案相同
craymichael

1
@craymichael不太。类似,我给你。区别在于可迭代。JanK的答案使用了解析器列表,而我的使用了subparsers.choices.items()。subparsers.choices.items()的优点是subparsers.choices.items()始终包含所有使用过的子解析器。使用列表时,必须在添加或删除子解析器时对其进行更改。我确实要感谢Jank的想法,我只是更进一步,因为我不喜欢硬编码和维护列表。
Gerard Kool

@杰拉德·库尔啊,我明白了。在寻找此答案时,我错过了!谢谢
craymichael

这对于1级解析器来说效果很好,但是如果您具有深层嵌套的命令树,则递归地获取子解析器可能会很困难。我创建了一些帮助器函数来简化
turtlemonvh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.