【问题标题】:Parsing non-mutually-exclusive groups of command-line arguments解析非互斥的命令行参数组
【发布时间】:2016-01-26 01:33:52
【问题描述】:

我正在尝试找到一种解析相关参数序列的方法,最好使用argparse

例如:

command --global-arg --subgroup1 --arg1 --arg2 --subgroup2 --arg1 --arg3 --subgroup3 --arg4 --subcommand1 --arg1 --arg3

其中--global-arg 适用于整个命令,但每个--subgroupN 参数都有仅适用于它的子参数(并且可能具有相同的名称,例如上面的--arg1--arg3),以及一些子参数是可选的,因此子参数的数量不是恒定的。但是,我知道每个 --subgroupN 子参数集是通过另一个 --subgroupN 的存在或参数列表的末尾来完成的(如果全局参数不能出现在末尾,我不会大惊小怪,尽管我想那是可能,只要它们不与子参数名称冲突)。

--subgroupN 元素本质上是子命令,但我似乎无法使用 argparse 的子解析器功能,因为它也吞下任何后续 --subgroupN 条目(因此会出现意外的 barfs论据)。

(xmlstarlet使用了这种风格的参数列表的一个例子)

除了编写我自己的解析器之外,还有什么建议吗?如果这是唯一的选择,我认为我至少可以利用 argparse 中的一些东西......

示例

下面的示例试图找到一种方法来解析参数结构,如下所示:

(a --name <name>|b --name <name>)+

在第一个示例中,我希望 --a 和 --b 引入一组由子解析器处理的参数。

我希望得到一些可能类似于

的东西
Namespace(a=Namespace(name="dummya"), b=Namespace(name="dummyb"))

子解析器示例失败

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_a = subparsers.add_parser("a")
parser_b = subparsers.add_parser("b")
parser_a.add_argument("--name")
parser_b.add_argument("--name")
parser.parse_args(["a", "--name", "dummy"])
> Namespace(name='dummy') (Good)
parser.parse_args(["b", "--name", "dummyb", "a", "--name", "dummya"])
> error: unrecognized arguments: a (BAD)

互斥组失败

parser = argparse.ArgumentParser()
g = parser.add_mutually_exclusive_group()
g1 = g.add_mutually_exclusive_group()
g1.add_argument("--name")
g2 = g.add_mutually_exclusive_group()
g2.add_argument("--name")
> ArgumentError: argument --name: conflicting option string(s): --name (BAD)

(我真的没想到这会奏效,我想看看我是否可以重复分组参数。)

【问题讨论】:

  • 请编辑您的问题以显示您尝试过的代码。这样你会得到更好的答案。
  • 之前至少有一个关于处理多个子命令的 SO 问题。解决方案涉及诸如递归子命令之类的事情。没有一个是 argparse 的直接用法。
  • 搜索[argparse] multiple 出现这样的帖子,stackoverflow.com/q/25318622/901925

标签: python-2.7 argparse


【解决方案1】:

除了子解析器机制之外,argparse 并非旨在处理参数组。除了nargs 分组,它按照它们出现在argv 列表中的顺序处理参数。

正如我在 cmets 中提到的,有一些较早的问题,可能可以通过搜索 multiple 之类的词找到。但是他们寻求一种或另一种方式来解决argparse的基本顺序无关设计。

https://stackoverflow.com/search?q=user%3A901925+[argparse]+multiple

我认为最直接的解决方案是事先处理sys.argv 列表,将其分成组,然后将这些子列表传递给一个或多个parsers

parse [command --global-arg], 
parse [--subgroup1 --arg1 --arg2], 
parse [--subgroup2 --arg1 --arg3], 
parse [--subgroup3 --arg4], 
parse [--subcommand1 --arg1 --arg3]

事实上,唯一的选择是使用该子解析器“slurp all else”的行为来获取可以再次解析的剩余参数。使用 parse_known_args 返回未知参数列表(如果该列表不为空,parse_args 会引发错误)。

【讨论】:

  • 好的 - 这听起来符合我的预期。我将深入研究以前的 cmets(我之前搜索时没有发现任何明显的东西),但至少我知道我的思路是正确的!
【解决方案2】:

使用上面的hpaulj's reply,我想出了以下内容:

args = [
    "--a", "--name", "dummya", 
    "--b", "--name", "dummyb",
    "--a", "--name", "another_a", "--opt"
]
parser_globals = argparse.ArgumentParser()
parser_globals.add_argument("--test")

parser_a = argparse.ArgumentParser()
parser_a.add_argument("--name")
parser_a.add_argument("--opt", action="store_true")

parser_b = argparse.ArgumentParser()
parser_b.add_argument("--name")

command_parsers = {
    "--a": parser_a,
    "--b": parser_b
}

the_namespace = argparse.Namespace()
if globals is not None:
    (the_namespace, rest) = parser_globals.parse_known_args(args)

subcommand_dict = vars(the_namespace)
subcommand = []
val = rest.pop()
while val:
    if val in command_parsers:
        the_args = command_parsers[val].parse_args(subcommand)
        if val in subcommand_dict:
            if "list" is not type(subcommand_dict[val]):
                subcommand_dict[val] = [subcommand_dict[val]]
            subcommand_dict[val].append(the_args)
        else:
            subcommand_dict[val] = the_args
        subcommand = []
    else:
        subcommand.insert(0, val)
    val = None if not rest else rest.pop()

我最终得到:

Namespace(
    --a=[
        Namespace(
            name='another_a',
            opt=True
        ),
        Namespace(
            name='dummya',
            opt=False
        )
    ],
    --b=Namespace(
        name='dummyb'
    ),
    test=None
)

这似乎符合我的目的。

【讨论】:

    猜你喜欢
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 2012-01-26
    相关资源
    最近更新 更多