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

Posted

技术标签:

【中文标题】Python argparse - 向多个子解析器添加参数【英文标题】:Python argparse - Add argument to multiple subparsers 【发布时间】:2011-11-21 20:27:22 【问题描述】:

我的脚本定义了一个主解析器和多个子解析器。我想将 -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

【问题讨论】:

相关:***.com/questions/14918804/… 【参考方案1】:

这可以通过定义一个包含常用选项的parent parser 来实现:

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'
<...>

此修复是在 this SO question 中提出的,它指的是跟踪拉取请求的问题。

@hpaulj 更新

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

【讨论】:

来自the docs:“当从子解析器请求帮助消息时,只会打印该特定解析器的帮助。帮助消息将不包括父解析器或兄弟解析器消息。”这似乎是该策略的一个主要缺点。 我最终向根解析器添加了一个虚拟参数,并带有一条帮助消息。 @RyneEverett:该手册部分令人困惑并且可能已经过时,因为至少在 Python 3.5.3 子解析器 --help 中似乎包含来自父解析器的参数。 我已经编辑了答案,显示了父子解析器的--help 输出。请注意create 的帮助消息同时显示“创建”和所有操作,这是不正确的。循环答案不会受到这个问题的影响 我认为它表现异常的原因是“父解析器”实际上与“带有子解析器的解析器”不同。 (请注意,文档从不显示一起使用的两个功能)很确定您需要一个***解析器,一个单独的“父”解析器,上面有全局参数,以及前者的子解析器,它们的 parents= 设置为后者.【参考方案2】:

accepted answer 是正确的;正确的方法是使用parent parsers。但是,示例代码 IMO 并没有真正解决问题。让我加上我的几分钱来提供一个更合适的例子。

与接受的答案的主要区别是明确可能有一些根级参数(如--verbose)以及仅用于一些子解析器的共享参数(-p 仅用于@ 987654327@ 和 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 命令/操作所独有的。我们解决方案的主要区别在于帮助的显示方式。这里是my output,这里是your output。 我完全同意,这不是必需的,但有些人可能想混合使用两者。您的帮助消息绝对更干净,感谢添加! 单独的父解析器是在多个子命令之间共享位置参数的唯一方法。 这是一个非常清晰的例子,这个答案需要更多人的支持。 @Elephant 不,子解析器的操作是互斥的。首先运行--verbose create -p 2,然后运行--verbose update -p 3【参考方案3】:

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

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")

【讨论】:

我认为这是一个不好的方法,因为您需要循环参数。 Sven Marnach 的答案更加干燥和可红色。【参考方案4】:

您可以通过以下方式循环您的子解析器。

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 不完全是。类似的,我给你。不同之处在于可迭代。 JanK 的答案使用解析器列表,我的使用 subparsers.choices.items()。 subparsers.choices.items() 的优点是 subparsers.choices.items() 总是包含所有使用的子解析器。使用列表时,您必须在添加或删除子解析器时更改它。我必须感谢 Jank 的想法,我只是更进一步,因为我不喜欢硬编码和维护列表。 @Gerald Kool 啊,我明白了。我在查看这个答案时错过了这一点!谢谢 这适用于 1 级解析器,但如果您有一个深度嵌套的命令树,递归地抓取子解析器可能会很困难。我在这里创建了一些辅助函数来简化此操作:github.com/turtlemonvh/argparse-subparser-filter 我喜欢这个,因为我可以在结尾写--verbose,而不是开头

以上是关于Python argparse - 向多个子解析器添加参数的主要内容,如果未能解决你的问题,请参考以下文章

Python argparse:存储参数不匹配任何子解析器

带有所需子解析器的 Argparse

带有通用子解析器命令的 Python argparse

如何从 Python 3 中的现有程序创建带有 argparse 的子解析器?

在子解析器 args 之后添加*** argparse 参数

argparse 可选子解析器(用于 --version)