Python argparse:复杂参数解析场景

5 投票
1 回答
2536 浏览
提问于 2025-04-18 01:49

我想实现一个命令行参数解析的场景:

我有四个参数:-g-wid-w1-w2

-w1-w2 总是一起出现。

-wid(-w1 -w2) 是互斥的,也就是说只能选一个,但必须有一个是必需的。

-g 是可选的;如果没有指定它,那么只能出现 (-w1 -w2),而不能出现 -wid

有没有一种优雅的方法可以用 argparse 来实现这个,但不使用子命令(我已经在使用子命令了)?

我在考虑自定义动作,但在动作的主体中,我需要知道它最后一次被调用的时间(也就是最后一个参数被解析时),但我不知道怎么做到,因为参数的数量和顺序可能会变化。

再多解释一下:我正在写的工具是用一个小部件和一个小工具参数 -g 来创建一个小工具。这个小部件要么是一个已有的小部件——然后通过它的 ID -wid 来引用,要么是一个使用参数 -w1-w2 创建的新小部件。如果没有指定 -g,那么这个工具将只使用 (-w1 -w2) 创建并存储一个新小部件,而不创建小工具。

谢谢!

1 个回答

2

如果你正确选择默认值,那么在调用'parse_args'之后,你可以轻松地测试'namespace'中参数值的逻辑组合。这个时候你也可以发出解析器错误。现在有一个bug报告在请求互相包含的组。在那里,我建议了一种添加通用组合测试的机制。但这仍然需要你自己进行逻辑测试。

http://bugs.python.org/issue11588 为argparse添加“必要包含”的组

我在那个问题中提出的解决方案的关键是让程序员可以使用一个seen_non_default_actions变量。这个变量实际上是一个动作的列表(实际上是一个集合),它记录了已经看到的动作(考虑到可选的位置参数总是被“看到”)。我希望能有更多关于如何实现包含和排除组合的讨论。

你需要指定:

我有4个参数:-g, -wid, -w1和-w2。 -w1和-w2总是一起出现 -wid和(-w1 -w2)是互斥的,但必须有一个出现 -g是可选的;如果没有指定,只有(-w1 -w2)可以出现,但不能有-wid

我会尝试总结为:

complex_group('g', required_next_exclusive_group('wid', inclusive_group('w1','w2)))

w1w2可以替换为'--w1w2',nargs=2。一个简单的'互相包含组'在这里可以工作。但argparse无法处理嵌套组。

widw1w2可以放在一个必需的互斥组中。

-g需要一个测试,比如if args.g is None and args.wid is None: error()


这里有一个脚本,它的行为符合你的要求(我认为),使用我在Issue11588中的最新补丁。act_w1等是实际的动作对象,它们可能出现在seen_actions列表中。测试函数与子解析器注册,并在其parse_know_args()的末尾执行。

parser = argparse.ArgumentParser(usage='custom usage')
sp = parser.add_subparsers(dest='cmd')
sp.required = True
spp = sp.add_parser('cmd1')
act_g = spp.add_argument('-g')
act_wid = spp.add_argument('--wid')
act_w1 = spp.add_argument('--w1')
act_w2 = spp.add_argument('--w2')

@spp.crosstest # decorator to register this function with spp
def test1(spp, seen_actions, *args):
    # seen_actions - list of actions that were seen by parser
    if 1==len({act_w1, act_w2}.intersection(seen_actions)):
        # error if only one of these was seen
        parser.error('-w1 and -w2 always appear together')
@spp.crosstest
def test2(spp, seen_actions, *args):
    # required mutually exclusive wid and (w1,w2 group)
    if act_wid in seen_actions:
        if act_w1 in seen_actions or act_w2 in seen_actions:
            parser.error('-wid and (-w1 -w2) are mutually exclusive')
    elif act_w1 not in seen_actions:
        parser.error('wid or (w1 and w2) required')
@spp.crosstest
def test3(spp, seen_actions, *args):
    # is this the simplest logical way of expressing this?
    if act_g not in seen_actions and act_wid in seen_actions:
        parser.error('not g, so not wid')
args = parser.parse_args()

在这个例子中,我保存并测试动作对象的存在。测试也可以使用dest字符串进行。我正在探索让这个测试更直观和用户友好的方法。扩展的装饰器集似乎最有前景。

撰写回答